控件带点击波纹的LinearLayout

/**
 * 一个特殊的LinearLayout,任何放入内部的clickable元素都具有波纹效果,当它被点击的时候,
 * 为了性能,尽量不要在内部放入复杂的元素
 * note: long click listener is not supported current for fix compatible bug.
 * @author xuanqis
 */
public class RevealLayout extends LinearLayout implements Runnable {

    private static final String TAG = "DxRevealLayout";
    private static final boolean DEBUG = true;

    /**
     * 画笔
     */

    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

    /**
     * 点击的View的宽高
     */
    private int mTargetWidth;
    private int mTargetHeight;

    /**
     * 点击的View的宽与高的最小值和最大值
     */
    private int mMinBetweenWidthAndHeight;
    private int mMaxBetweenWidthAndHeight;



    /**
     * 点击的中心点所在的坐标
     */
    private float mCenterX;
    private float mCenterY;


    /**
     * 半径最大值、半径单位变化值、当前的半径、父view的左上角的坐标数组
     */
    private int mMaxRevealRadius;
    private int mRevealRadiusGap;
    private int mRevealRadius = 0;
    private int[] mLocationInScreen = new int[2];

    private boolean mShouldDoAnimation = false;
    private boolean mIsPressed = false;
    private int INVALIDATE_DURATION = 40;



    private View mTouchTarget;
    private DispatchUpTouchEventRunnable mDispatchUpTouchEventRunnable =
            new DispatchUpTouchEventRunnable();

    public RevealLayout(Context context) {
        super(context);
        init();
    }

    public RevealLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public RevealLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    /**
     * 进行初始化工作,设置画笔的颜色
     */
    private void init() {
        //有些ViewGroup设置了不能绘制,要将其进行重置
        setWillNotDraw(false);
        //设置画笔的颜色
        mPaint.setColor(getResources().getColor(R.color.reveal_color));
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        this.getLocationOnScreen(mLocationInScreen);
    }

    /**
     * 根据点击的位置,确定波纹的最大半径,还有其他的初始半径等也初始化
     * @param event 点击事件,需要事件的坐标
     * @param view 进行初始化的View
     */
    private void initParametersForChild(MotionEvent event, View view) {

        //这里是相对于点击View的相对坐标,这个不知道为啥好像是相对于RevealLayout的坐标
        mCenterX = event.getX();
        mCenterY = event.getY();

        mTargetWidth = view.getMeasuredWidth();
        mTargetHeight = view.getMeasuredHeight();

        mMinBetweenWidthAndHeight = Math.min(mTargetWidth, mTargetHeight);
        mMaxBetweenWidthAndHeight = Math.max(mTargetWidth, mTargetHeight);

        //初始半径为0
        mRevealRadius = 0;
        //。。。
        mShouldDoAnimation = true;
        //。。。
        mIsPressed = true;
        //增加的单位是宽度和高度最小值的 1/8
        mRevealRadiusGap = mMinBetweenWidthAndHeight / 8;


        //这里以后看到了再看一遍,感觉是有点问题,不对劲

        int[] location = new int[2];
        view.getLocationOnScreen(location);
        // left:点击的View和父View的x坐标的差值
        int left = location[0] - mLocationInScreen[0];
        //transformedCenterX: 点击的点相对于View的偏移 - view相对于父View的偏移
        int transformedCenterX = (int) mCenterX - left;
        //最长的半径的值为
        mMaxRevealRadius = Math.max(transformedCenterX, mTargetWidth - transformedCenterX);
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        //播放为为false、目标View的宽度为0、目标View为null
        if (!mShouldDoAnimation || mTargetWidth <= 0 || mTouchTarget == null) {
            return;
        }

        //增大半径,有点牛逼,一次到位
        if (mRevealRadius > mMinBetweenWidthAndHeight / 2) {
            mRevealRadius += mRevealRadiusGap * 4;
        } else {
            mRevealRadius += mRevealRadiusGap;
        }

        this.getLocationOnScreen(mLocationInScreen);
        int[] location = new int[2];
        mTouchTarget.getLocationOnScreen(location);
        //画布的显示范围
        int left = location[0] - mLocationInScreen[0];
        int top = location[1] - mLocationInScreen[1];
        int right = left + mTouchTarget.getMeasuredWidth();
        int bottom = top + mTouchTarget.getMeasuredHeight();

        //保存现有状态
        canvas.save();
        //设置画布的显示范围
        canvas.clipRect(left, top, right, bottom);
        //绘制波纹
        canvas.drawCircle(mCenterX, mCenterY, mRevealRadius, mPaint);
        //restore和save要配对使用
        canvas.restore();

        if (mRevealRadius <= mMaxRevealRadius) {
            //在指定的时间后,将指定的矩形进行刷新
            postInvalidateDelayed(INVALIDATE_DURATION, left, top, right, bottom);
        } else if (!mIsPressed) {
            //不需要继续进行绘制了
            mShouldDoAnimation = false;
            postInvalidateDelayed(INVALIDATE_DURATION, left, top, right, bottom);
        }
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        //得到相对于屏幕的绝对地址
        int x = (int) event.getRawX();
        int y = (int) event.getRawY();

        int action = event.getAction();
        if (action == MotionEvent.ACTION_DOWN) {
            //得到点击事件所在的具体的View
            View touchTarget = getTouchTarget(this, x, y);

            if (touchTarget != null && touchTarget.isClickable() && touchTarget.isEnabled()) {
                mTouchTarget = touchTarget;
                //使用事件去初始化View的相关信息
                initParametersForChild(event, touchTarget);
                //等40ms进行重绘制
                postInvalidateDelayed(INVALIDATE_DURATION);
            }
        } else if (action == MotionEvent.ACTION_UP) {
            mIsPressed = false;
            //抬起进行重绘,颜色消失
            postInvalidateDelayed(INVALIDATE_DURATION);

            mDispatchUpTouchEventRunnable.event = event;
            postDelayed(mDispatchUpTouchEventRunnable, 200);
            return true;
        } else if (action == MotionEvent.ACTION_CANCEL) {
            mIsPressed = false;
            postInvalidateDelayed(INVALIDATE_DURATION);
        }

        return super.dispatchTouchEvent(event);
    }

    /**
     * 得到ViewGroup的点击的点在哪个孩子中
     * @param view
     * @param x
     * @param y
     * @return 点所在的孩子
     */
    private View getTouchTarget(View view, int x, int y) {
        View target = null;
        ArrayList<View> TouchableViews = view.getTouchables();
        for (View child : TouchableViews) {
            if (isTouchPointInView(child, x, y)) {
                target = child;
                break;
            }
        }

        return target;
    }

    /**
     * 判断点击的点(x,y)是否在View内
     * @param view 判断的View
     * @param x x坐标
     * @param y y坐标
     * @return 点是否在View内
     */
    private boolean isTouchPointInView(View view, int x, int y) {
        int[] location = new int[2];
        view.getLocationOnScreen(location);
        int left = location[0];
        int top = location[1];
        int right = left + view.getMeasuredWidth();
        int bottom = top + view.getMeasuredHeight();
        return view.isClickable() && y >= top && y <= bottom
                && x >= left && x <= right;
    }

    @Override
    public boolean performClick() {
        postDelayed(this, 400);
        return true;
    }

    @Override
    public void run() {
        super.performClick();
    }

    private class DispatchUpTouchEventRunnable implements Runnable {
        public MotionEvent event;

        @Override
        public void run() {
            if (mTouchTarget == null || !mTouchTarget.isEnabled()) {
                return;
            }

            if (isTouchPointInView(mTouchTarget, (int) event.getRawX(), (int) event.getRawY())) {
                mTouchTarget.performClick();
            }
        }
    }

}
发表在 未分类 | 留下评论

外部拦截的横向LinearLayout的特例

/**
 * 横向的LinearLayout
 */
public class HorizontalScrollViewEx extends ViewGroup {
    private static final String TAG = "HorizontalScrollViewEx";

    private int mChildrenSize;
    private int mChildWidth;
    private int mChildIndex;

    /**
     * 分别记录上次滑动的坐标
     * 上一个事件的x和y坐标
     */

    private int mLastX = 0;
    private int mLastY = 0;

    /**
     * 分别记录上次滑动的坐标(onInterceptTouchEvent)
     */
    private int mLastXIntercept = 0;
    private int mLastYIntercept = 0;

    private Scroller mScroller;
    private VelocityTracker mVelocityTracker;

    public HorizontalScrollViewEx(Context context) {
        super(context);
        init();
    }

    public HorizontalScrollViewEx(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public HorizontalScrollViewEx(Context context, AttributeSet attrs,int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    /**
     * 初始化工作,创建滑动器和速度跟踪器
     */
    private void init() {
        //创建一个滑动器
        mScroller = new Scroller(getContext());
        //创建一个速度跟踪器
        mVelocityTracker = VelocityTracker.obtain();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        boolean intercepted = false;
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                intercepted = false;
                //如果是外部正在滚动,那么
                //1. 横向,综合两次的进行滑动
                //2. 纵向滑动,不处理
                if (!mScroller.isFinished()) {
                    mScroller.abortAnimation();
                    intercepted = true;
                }
                break;
            }
            //判断自己拦不拦截,拦截了得话,那么之后的事件也由当前处理
            case MotionEvent.ACTION_MOVE: {
                int deltaX = x - mLastXIntercept;
                int deltaY = y - mLastYIntercept;
                intercepted = Math.abs(deltaX) > Math.abs(deltaY);
                break;
            }
            //不拦截松开动作
            case MotionEvent.ACTION_UP: {
                intercepted = false;
                break;
            }
            default:
                break;
        }

        Log.d(TAG, "intercepted=" + intercepted);
        mLastX = x;
        mLastY = y;
        mLastXIntercept = x;
        mLastYIntercept = y;

        return intercepted;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //添加事件,进行监听
        mVelocityTracker.addMovement(event);

        //得到x和y坐标
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                if (!mScroller.isFinished()) {
                    Log.d(TAG, "关闭");
                    mScroller.abortAnimation();
                }
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                int deltaX = x - mLastX;
                int deltaY = y - mLastY;
                //滑多远就跟着走多远
                scrollBy(-deltaX, 0);
                break;
            }
            //离开屏幕,进行页面的滚动计算
            case MotionEvent.ACTION_UP: {
                //返回离原位置滑动了多远
                int scrollX = getScrollX();
                int scrollToChildIndex = scrollX / mChildWidth;
                //以秒计算速度
                mVelocityTracker.computeCurrentVelocity(1000);
                //得到速度
                float xVelocity = mVelocityTracker.getXVelocity();
                if (Math.abs(xVelocity) >= 50) {
                    mChildIndex = xVelocity > 0 ? mChildIndex - 1 : mChildIndex + 1;
                } else {
                    //这里好像有点蠢
                    mChildIndex = (scrollX + mChildWidth / 2) / mChildWidth;
                }
                mChildIndex = Math.max(0, Math.min(mChildIndex, mChildrenSize - 1));
                int dx = mChildIndex * mChildWidth - scrollX;
                smoothScrollBy(dx, 0);
                mVelocityTracker.clear();
                break;
            }
            default:
                break;
        }

        mLastX = x;
        mLastY = y;
        return true;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int measuredWidth;
        int measuredHeight;
        //得到子控件的个数
        final int childCount = getChildCount();
        //先调用子控件的测量方法
        measureChildren(widthMeasureSpec, heightMeasureSpec);

        //得到当前控件的宽度和高度的测量模式和测量大小
        int widthSpaceSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSpaceSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);

        //没有孩子,当前的View Group宽度和高度由孩子决定,所以也就是0
        if (childCount == 0) {
            setMeasuredDimension(0, 0);
        }
        //
        else if (heightSpecMode == MeasureSpec.AT_MOST && widthSpecMode==MeasureSpec.AT_MOST){
            final View childView = getChildAt(0);
            measuredWidth = childView.getMeasuredWidth() * childCount;
            measuredHeight = childView.getMeasuredHeight();
            setMeasuredDimension(measuredWidth, measuredHeight);
        }
        else if (heightSpecMode == MeasureSpec.AT_MOST) {
            final View childView = getChildAt(0);
            measuredHeight = childView.getMeasuredHeight();
            setMeasuredDimension(widthSpaceSize, measuredHeight);
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            final View childView = getChildAt(0);
            measuredWidth = childView.getMeasuredWidth() * childCount;
            setMeasuredDimension(measuredWidth, heightSpaceSize);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        /**
         * 布局的工作其实也就把对应的子元素放到对应的位置就可以了
         * 其他倒是没啥了
         */
        int childLeft = 0;
        final int childCount = getChildCount();
        mChildrenSize = childCount;

        for (int i = 0; i < childCount; i++) {
            final View childView = getChildAt(i);
            if (childView.getVisibility() != View.GONE) {
                final int childWidth = childView.getMeasuredWidth();
                mChildWidth = childWidth;
                childView.layout(childLeft, 0, childLeft + childWidth,
                        childView.getMeasuredHeight());
                childLeft += childWidth;
            }
        }
    }

    /**
     * 弹性的进行500ms滑动x方向dx, y方向dy
     * @param dx
     * @param dy
     */
    private void smoothScrollBy(int dx, int dy) {
        mScroller.startScroll(getScrollX(), 0, dx, 0, 500);
        invalidate();
    }

    @Override
    public void computeScroll() {
        //配合smoothScrollBy方法来进行弹性滑动
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        //控件从window上面删除,速度跟踪回收
        mVelocityTracker.recycle();
        super.onDetachedFromWindow();
    }

}
发表在 未分类 | 留下评论

内部拦截的ListView

/**
 * 内部拦截的ListView
 */
public class ListViewEx extends ListView {
    private static final String TAG = "ListViewEx";


    /**
     * 分别记录上次滑动的坐标
     */
    private int mLastX = 0;
    private int mLastY = 0;

    public ListViewEx(Context context) {
        super(context);
    }

    public ListViewEx(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ListViewEx(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setHorizontalScrollViewEx2(
            HorizontalScrollViewEx2 horizontalScrollViewEx2) {
//        mHorizontalScrollViewEx2 = horizontalScrollViewEx2;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();

        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN: {
            //把父ViewGroup设为不拦截
            getParent().requestDisallowInterceptTouchEvent(true);
            break;
        }
        case MotionEvent.ACTION_MOVE: {
            int deltaX = x - mLastX;
            int deltaY = y - mLastY;
            Log.d(TAG, "dx:" + deltaX + " dy:" + deltaY);
            //父控件进行拦截,注意这里的getParent得到的不一定是个View
            if (Math.abs(deltaX) > Math.abs(deltaY)) {
                getParent().requestDisallowInterceptTouchEvent(false);
            }
            break;
        }
        case MotionEvent.ACTION_UP: {
            break;
        }
        default:
            break;
        }

        mLastX = x;
        mLastY = y;
        return super.dispatchTouchEvent(event);
    }

}

发表在 未分类 | 留下评论

可以拖动的TextView

/**
 * 可以拖动的TextView,中间尝试使用scrollTo,没成功
 * 因为是继承的TextView,所以wrap_content的问题解决
 * Action_down和Action_up交给父类处理
 * @author xuanqis
 */
public class TestButton extends android.support.v7.widget.AppCompatTextView {
    private static final String TAG = "TestButton";
    /**
     * 能够认为发生了滑动的最短距离,单位是像素
     */
    private int mScaledTouchSlop;
    /**
     * 分别记录上次滑动的坐标
     */
    private int mLastX = 0;
    private int mLastY = 0;

    public TestButton(Context context) {
        this(context, null);
    }

    public TestButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public TestButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    /**
     * 得到发生滑动的最小距离
     */
    private void init() {
        //得到系统的滑动距离
        mScaledTouchSlop = ViewConfiguration.get(getContext())
                .getScaledTouchSlop();
        Log.d(TAG, "sts:" + mScaledTouchSlop);
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //得到的是相对于屏幕左上角的坐标
        int x = (int) event.getRawX();
        int y = (int) event.getRawY();

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN: {
                super.onTouchEvent(event);  
                break;
            }

            case MotionEvent.ACTION_MOVE: {
                //偏移量=现在-原来
                int deltaX = x - mLastX;
                int deltaY = y - mLastY;
                Log.d(TAG, "move, deltaX:" + deltaX + " deltaY:" + deltaY);
                //偏移量进行相加,进行移动
                int translationX = (int) ViewHelper.getTranslationX(this) + deltaX;
                int translationY = (int) ViewHelper.getTranslationY(this) + deltaY;
                ViewHelper.setTranslationX(this, translationX);
                ViewHelper.setTranslationY(this, translationY);
                break;
            }
            case MotionEvent.ACTION_UP: {
                super.onTouchEvent(event);
                break;
            }
            default:
                break;
        }

        mLastX = x;
        mLastY = y;
        return true;
    }
}


发表在 未分类 | 留下评论

binder连接池

  1. aidl文件
// IBinderPool.aidl
package com.example.a2.bindpool;


interface IBinderPool {

    IBinder queryBinder(int binderCode);
}

// ICompute.aidl
package com.example.a2.bindpool;

interface ICompute {
    int add(int a, int b);
}

// ISecurityCenter.aidl
package com.example.a2.bindpool;

//用来进行加密的
interface ISecurityCenter {
    String encrypt(String content);
    String decrypt(String password);
}
  1. Binder的实现
public class ComputImpl extends ICompute.Stub {
    @Override
    public int add(int a, int b) throws RemoteException {
        return a + b;
    }
}

public class SecurityCenterImpl extends ISecurityCenter.Stub {

    private static final char SECRET_CODE='^';
    @Override
    public String encrypt(String content) throws RemoteException {
        char[] chars =content.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            chars[i] ^= SECRET_CODE;
        }
        return new String(chars);
    }

    @Override
    public String decrypt(String password) throws RemoteException {
        return encrypt(password);
    }
}

  1. Bindpool的封装类
/**
 * @author xuanqis
 * 单例模式,设为volatile,防止指令重排序
 * 具有服务端BinderPoolImpl的代理
 *
 */
public class BinderPool {
    private static final String TAG = "BinderPool";
    public static final int BIND_NONE = -1;
    public static final int BIND_COMPUT = 0;
    public static final int BIND_SECURITY_CENTER = 1;

    /**
     * 客户端的ApplicationContext
     */
    private Context mContext;

    /**
     * 客户端的代理BinderPool的Proxy,封装在BinderPool类里面
     */
    private IBinderPool mBinderPool;
    /**
     * 单例,懒加载模式的加载,这里设置为volatile防止指令重排序
     */
    private static volatile BinderPool sInstance;
    /**
     * 用来控制同步,回调onServiceConnected是异步的
     */
    private CountDownLatch mconnectBinderPoolCountDownLatch;

    /**
     * 对于每个进程,有唯一的一个服务端BinderPoolImpl的代理,注意这里单例的多线程处理
     * @param context 活动的上下文,如果当前进程没有Binder池的代理的话,就利用这个上下文进行创建
     * @return BinderPool类的单例
     */
    public static BinderPool getInstance(Context context){
        //处理多线程的问题,首先判断是不是null,是的话就获取锁,再判断是不是null,再创建单例
        if (sInstance==null){
            synchronized (BinderPool.class){
                if (sInstance==null){
                    sInstance=new BinderPool(context);
                }
            }
        }
        return sInstance;
    }

    /**
     * 一个活动创建Binder池就会直接将活动所在的Application与服务连接起来
     * @param context 活动上下文
     */
    private BinderPool(Context context){
        mContext=context.getApplicationContext();
        connectBinderPoolService();
    }

    /**
     * 将活动的应用和服务进行绑定,执行之后就有BinderPool的代理了,客户端
     */
    private synchronized void connectBinderPoolService(){
        mconnectBinderPoolCountDownLatch = new CountDownLatch(1);
        Intent intent = new Intent(mContext, BinderPoolService.class);
        mContext.bindService(intent, mBinderPoolConnection, Context.BIND_AUTO_CREATE);
        try{
            //等待绑定完成,才执行结束
            mconnectBinderPoolCountDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     *BinderPool的实现类,运行在服务端,静态内部类,只会有一个
     */
    public static class BinderPoolImpl extends IBinderPool.Stub{
        @Override
        public IBinder queryBinder(int binderCode) throws RemoteException {
            Binder binder = null;
            switch (binderCode) {
                case BIND_SECURITY_CENTER:
                    binder=new SecurityCenterImpl();
                    break;
                case BIND_COMPUT:
                    binder=new ComputImpl();
                    break;
                default:
                    break;
            }
            return binder;
        }
    }

    /**
     * 连接类,其实是将服务器的Binder封装到了客户端的BinderPool里面
     */
    private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinderPool = IBinderPool.Stub.asInterface(service);
            try {
                mBinderPool.asBinder().linkToDeath(mDeathRecipient, 0);

            } catch (RemoteException e) {
                e.printStackTrace();
            }
            mconnectBinderPoolCountDownLatch.countDown();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    /**
     * 失败重连,运行在线程池里面
     */
    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            Log.d(TAG, "bind died...");
            mBinderPool.asBinder().unlinkToDeath(mDeathRecipient, 0);
            mBinderPool=null;
            connectBinderPoolService();
        }
    };

    public IBinder queryBinder(int binderCode){
        IBinder binder = null;
        try{
            if (mBinderPool != null){
                binder=mBinderPool.queryBinder(binderCode);
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        return binder;
    }

}
  1. Service服务,返回连接池Binder
public class BinderPoolService extends Service {
    private static final String TAG = "BinderPoolService";
    private Binder mBinderPool = new BinderPool.BinderPoolImpl();

    @Override
    public IBinder onBind(Intent intent) {
        //返回binder连接池的binder
        return mBinderPool;
    }
}
  1. 测试连接池的活动
public class BindpoolActivity extends AppCompatActivity {
    private static final String TAG ="BinderPoolActivity";
    /**
     * Binder连接池中的Binder代理
     */
    private ISecurityCenter mSecurityCenter;
    private ICompute mCompute;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bindpool);
        new Thread(new Runnable() {
            @Override
            public void run() {
                doWork();
            }
        }).start();
    }

    private void doWork(){
        //得到当前进程的Binder连接池
        BinderPool binderPool=BinderPool.getInstance(this);

        IBinder securityBinder = binderPool.queryBinder(BinderPool.BIND_SECURITY_CENTER);
        mSecurityCenter=SecurityCenterImpl.asInterface(securityBinder);
        Log.d(TAG, "visit IsecurityBinder");
        String msg = "hello android...";
        Log.d(TAG, "content:"+msg);
        try{
            String password = mSecurityCenter.encrypt(msg);
            Log.d(TAG, "加密:"+password);
            Log.d(TAG, "解密:"+mSecurityCenter.decrypt(password));
        } catch (RemoteException e) {
            e.printStackTrace();
        }

        Log.d(TAG, "visit icompute...");
        IBinder computeBinder = binderPool.queryBinder(BinderPool.BIND_COMPUT);
        mCompute=ComputImpl.asInterface(computeBinder);
        try{
            Log.d(TAG, "3+5="+mCompute.add(3,5));
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}
发表在 Android | 留下评论

TCP通信

  1. 注意不能在主线程中进行网络操作,不能在子线程更新UI。

  2. 活动的布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffff"
    android:orientation="vertical"
    android:padding="5dp" >

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >

            <TextView
                android:id="@+id/msg_container"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </LinearLayout>
    </ScrollView>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <EditText
            android:id="@+id/msg"
            android:layout_width="0dp"
            android:layout_height="35dp"
            android:layout_weight="1"
            android:background="@drawable/edit"
            android:ems="10"
            android:padding="5dp" />

        <Button
            android:id="@+id/send"
            android:layout_width="wrap_content"
            android:layout_height="35dp"
            android:enabled="false"
            android:text="发送" />
    </LinearLayout>

</LinearLayout>
  1. 服务端
public class TCPServerService extends Service {
    private static final String TAG = "TCPServerService";
    /**
     * 启动的线程已经脱离服务的主线程了,
     */
    private boolean mIsServerDestroyed = false;
    private String[] mDefinedMessages = new String[]{
            "你好呀,哈哈",
            "请问你叫什么名字",
            "今天的天气不错",
            "你知道吗,我可以和多个人聊天哦",
            "给你讲个笑话吧,据说爱笑的人运气不会太差,不知道真假"
    };

    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate");
        super.onCreate();
        Log.d(TAG, "onCreate");
        new Thread(new TcpServer()).start();
    }

    public TCPServerService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        mIsServerDestroyed=true;
        super.onDestroy();
    }

    private class TcpServer implements Runnable{
        @Override
        public void run() {
            ServerSocket serverSocket = null;
            try{
                //建立服务器的端口
                serverSocket=new ServerSocket(8888);
            } catch (IOException e) {
                e.printStackTrace();
                Log.d(TAG, "establish tcp server failed, port:8888");
                return ;
            }
            if (serverSocket!=null){
                Log.d(TAG, "服务器端口成功...");
            }
            //如果没有服务没有被杀死
            while(!mIsServerDestroyed){
                try{
                    //得到和客户端的连接端口
                    final Socket client = serverSocket.accept();
                    Log.d(TAG, "accept socket");
                    new Thread(){
                        @Override
                        public void run() {
                            try{
                                //开个新的线程,在这个新的线程里面进行和客户的交流
                                responseClient(client);
                            }catch (IOException e){
                                e.printStackTrace();
                            }
                        }
                    }.start();
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.d(TAG, "连接失败");
                }
            }
        }
    }

    /**
     * 进行和客户端的交流
     * @param client 和客户端连接的端口
     * @throws IOException
     */
    private void responseClient(Socket client) throws IOException{
        BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
        PrintWriter out = new PrintWriter(new OutputStreamWriter(client.getOutputStream()));
        Log.d(TAG, out.toString());
        out.println("欢迎来到聊天室");
        out.flush();
        while(!mIsServerDestroyed){
            String str = in.readLine();
            Log.d(TAG, "msg from client:"+str);
            if (str == null){
                break;
            }
            int i = new Random().nextInt(mDefinedMessages.length);
            String msg = mDefinedMessages[i];
            out.println(msg);
            out.flush();
            Log.d(TAG, "send : "+msg);
        }
        Log.d(TAG, "client quit...");
        MyUtils.close(in);
        MyUtils.close(out);
        client.close();
    }
}
  1. 活动
public class TcpClientActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "TCPClientActivity";
    private static final int MSG_RECEIVE_NEW_MSG = 1;
    private static final int MSG_SOCKET_CONNECTED = 2;

    private Button mSendButton;
    private TextView mMessageTextView;
    private EditText mMessageEditText;

    private PrintWriter mPrintWriter;
    private Socket mClientSocket;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_RECEIVE_NEW_MSG:
                    mMessageTextView.setText(mMessageTextView.getText() + (String) msg.obj);
                    break;
                case MSG_SOCKET_CONNECTED:
                    mSendButton.setEnabled(true);
                    break;
                default:
                    break;
            }
            super.handleMessage(msg);
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tcp_client);
        Log.d(TAG, "onCreate");
        mMessageTextView = findViewById(R.id.msg_container);
        mSendButton = findViewById(R.id.send);
        mSendButton.setOnClickListener(this);
        mMessageEditText = findViewById(R.id.msg);
        Intent intent = new Intent(this, TCPServerService.class);
        startService(intent);
        Log.d(TAG, "onCreate");
        new Thread() {
            @Override
            public void run() {
                connectTCPServer();
            }
        }.start();
    }

    @Override
    protected void onDestroy() {
        if (mClientSocket != null) {
            try {
                //连接的关闭要先把流给杀死,然后才能关闭连接
                mClientSocket.shutdownInput();
                mClientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        super.onDestroy();
    }

    private void connectTCPServer() {
        Socket socket = null;
        while (socket == null) {
            try {
                socket = new Socket("localhost", 8888);
                mClientSocket = socket;

                mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
                        socket.getOutputStream()
                )), true);
                mHandler.sendEmptyMessage(MSG_SOCKET_CONNECTED);

            } catch (IOException e) {
                e.printStackTrace();
                SystemClock.sleep(1000);
                Log.d(TAG, "connect server failed, retry...");
            }

        }
        Log.d(TAG, "connect server success. ");
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    mClientSocket.getInputStream()
            ));

            Log.d(TAG, br.toString());
            while (!TcpClientActivity.this.isFinishing()) {
                Log.d(TAG, "qian");
                String msg = br.readLine();
                Log.d(TAG, "hou");
                Log.d(TAG, "receiver:" + msg);
                if (msg != null) {
                    String time = fromatDateTime(System.currentTimeMillis());
                    final String showMsg = "server " + time + ":" + msg + "\n";
                    mHandler.obtainMessage(MSG_RECEIVE_NEW_MSG, showMsg)
                            .sendToTarget();
                }
            }
            Log.d(TAG, "quit...");
            MyUtils.close(mPrintWriter);
            MyUtils.close(br);
            socket.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private String fromatDateTime(long time) {
        return new SimpleDateFormat("(HH:mm:ss)").format(new Date(time));
    }


    @Override
    public void onClick(View v) {
        if (v == mSendButton) {
            Log.d(TAG, "点击了");
            final String msg = mMessageEditText.getText().toString();
            if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {
                new Thread(){
                    @Override
                    public void run() {
                        mPrintWriter.println(msg);
                    }
                }.start();
                mMessageEditText.setText("");
                String time = fromatDateTime(System.currentTimeMillis());
                final String showedMsg = "self" + time + ":" + msg + "\n";
                mMessageTextView.setText(mMessageTextView.getText() + showedMsg);
            }
        }
    }
}
发表在 未分类 | 留下评论

aidl书籍管理(有取消注册)

  1. aidl
// Book.aidl
package com.example.a2.aidl;

parcelable Book;

// IBookManager.aidl
package com.example.a2.aidl;

import com.example.a2.aidl.Book;
import com.example.a2.aidl.IOnNewBookArrivedListener;
interface IBookManager {
    List<Book> getBookList();

    void addBook(in Book book);

    void registerListener(IOnNewBookArrivedListener listener);
    void unRegisterListener(IOnNewBookArrivedListener listener);
}


// IOnNewBookArrivedListener.aidl
package com.example.a2.aidl;

import com.example.a2.aidl.Book;

interface IOnNewBookArrivedListener {
    void onNewBookArrived(in Book book);
}
  1. book类
public class Book implements Parcelable {
    public int booId;
    public String bookName;

    public Book(int booId, String bookName){
        this.booId=booId;
        this.bookName=bookName;
    }
    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(booId);
        dest.writeString(bookName);
    }

    public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>(){

        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    private Book(Parcel in){
        booId=in.readInt();
        bookName=in.readString();
    }

    @Override
    public String toString() {
        return "bookId: "+booId +" bookName: "+bookName;
    }
}
  1. 服务端服务
/**
 *
 * @author xuanqis
 * 服务端服务,进行书籍的管理,可以取消注册监听器
 */
public class BookManagerServer extends Service {

    private static final String TAG = "BookManagerServer";

    /**
     * 判断服务是否已经死亡的原子变量,用在添加书籍的线程
     */
    private AtomicBoolean isServiceDestroy = new AtomicBoolean(false);
    /**
     * 书籍的列表,这里虽然是CopyOnWriteArrayList,但是在传递到客户端的过程中会转化为ArrayList
     * CopyOnWriteArrayList不是ArrayList的子类
     */
    private CopyOnWriteArrayList<Book> mBookLists = new CopyOnWriteArrayList<>();
    /**
     * 管理监听器的表,可以根据binder来对应监听器,同一个binder在里面只会有一个监听器
     */
    private RemoteCallbackList<IOnNewBookArrivedListener> mListeners=
            new RemoteCallbackList<>();

    private Binder mBinder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookLists;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookLists.add(book);
        }

        @Override
        public void registerListener(com.example.a2.aidl.IOnNewBookArrivedListener listener)
                throws RemoteException {
            mListeners.register(listener);
        }

        @Override
        public void unRegisterListener(com.example.a2.aidl.IOnNewBookArrivedListener listener)
                throws RemoteException {
            mListeners.unregister(listener);
        }
    };
    public BookManagerServer() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mBookLists.add(new Book(1, "Android"));
        mBookLists.add(new Book(2, "IOS"));
        new Thread(new ServerWorker()).start();
    }


    private void onNewBookArrived(Book book )throws RemoteException{
        mBookLists.add(book);

        final int N = mListeners.beginBroadcast();
        Log.d(TAG, "开始逐个进行监听器的开启:"+N);
        for (int i = 0; i < N; i++) {
            IOnNewBookArrivedListener listener = mListeners.getBroadcastItem(i);
            if (listener!=null){
                try{
                    listener.onNewBookArrived(book);
                }catch (RemoteException e){
                    e.printStackTrace();
                }
            }
        }
        mListeners.finishBroadcast();
    }

    /**
     * 每隔5s添加一本书籍
     */
    private class ServerWorker implements Runnable{
        @Override
        public void run() {
            while(!isServiceDestroy.get()){
                try{
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int bookId = mBookLists.size()+1;
                Book newBook = new Book(bookId, "new Book# "+bookId);
                try{
                    onNewBookArrived(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

        }
    }

}
  1. 客户端活动
/**
 * @author xuanqis
 * 客户端活动,能够增加书籍,查询书籍列表,在新书到达时接受书籍信息
 */
public class BookManagerActivity extends AppCompatActivity {

    private static final String TAG = "BookManagerActivity";
    //新书到达的消息what
    private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;

    private IBookManager bookManager;
    /**
     * 在主线程处理消息
     */
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_NEW_BOOK_ARRIVED:
                    Log.d(TAG, "新来了一本书:" + msg.obj);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    };
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            bookManager = IBookManager.Stub.asInterface(service);
            try {
                List<Book> list = bookManager.getBookList();
                Log.i(TAG, "query book list, list type:" + list.getClass());
                Log.i(TAG, "query book list, list type:" + list.toString());
                Book book = new Book(3, "Android进阶");
                bookManager.addBook(book);
                Log.d(TAG, "加入:" + book);
                list = bookManager.getBookList();
                Log.d(TAG, "书单:" + list.toString());
                bookManager.registerListener(mIOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            bookManager = null;
            Log.e(TAG, "连接关闭.");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_book_manager);
        //绑定客户端和服务端
        Intent intent = new Intent(this, BookManagerServer.class);
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        //取消注册监听器
        if (bookManager != null && bookManager.asBinder().isBinderAlive()) {
            try {
                bookManager.unRegisterListener(mIOnNewBookArrivedListener);
                Log.d(TAG, "解除注册" + bookManager);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        //解除绑定
        unbindService(mServiceConnection);
        super.onDestroy();
    }


    /**
     * 新书到达的监听器,在客户端进程执行
     */
    private IOnNewBookArrivedListener mIOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
        @Override
        public void onNewBookArrived(Book book) throws RemoteException {
            //这里的book是放在obj里面,要注意的是obj的跨进程只能支持系统设置的Parcelable类
            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, book).sendToTarget();
        }
    };

}
发表在 Android | 留下评论

aidl书籍管理(无取消注册)

  1. book
public class Book implements Parcelable {
    public int booId;
    public String bookName;

    public Book(int booId, String bookName){
        this.booId=booId;
        this.bookName=bookName;
    }
    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(booId);
        dest.writeString(bookName);
    }

    public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>(){

        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    private Book(Parcel in){
        booId=in.readInt();
        bookName=in.readString();
    }

    @Override
    public String toString() {
        return "bookId: "+booId +" bookName: "+bookName;
    }
}
  1. 服务端
/**
 *
 * @author xuanqis
 * 服务端服务,进行书籍的管理,无法取消注册监听器
 */
public class BookManagerServer extends Service {

    private static final String TAG = "BookManagerServer";

    /**
     * 判断服务是否已经死亡的原子变量,用在添加书籍的线程
     */
    private AtomicBoolean isServiceDestroy = new AtomicBoolean(false);
    /**
     * 书籍的列表,这里虽然是CopyOnWriteArrayList,但是在传递到客户端的过程中会转化为ArrayList
     * CopyOnWriteArrayList不是ArrayList的子类
     */
    private CopyOnWriteArrayList<Book> mBookLists = new CopyOnWriteArrayList<>();
    /**
     * 这里需要改,这个并不能实现移除监听器的功能,因为客户端监听器在服务端生成的是代理
     */
    private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListeners =
            new CopyOnWriteArrayList<>();

    private Binder mBinder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookLists;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookLists.add(book);
        }

        @Override
        public void registerListener(com.example.a2.aidl.IOnNewBookArrivedListener listener) throws RemoteException {
            if (!mListeners.contains(listener)){
                mListeners.add(listener);
            }
            else {
                Log.i(TAG, "已经存在了");
            }
            Log.i(TAG, "监听器的个数:"+mListeners.size());
        }

        @Override
        public void unRegisterListener(com.example.a2.aidl.IOnNewBookArrivedListener listener) throws RemoteException {
            if (mListeners.contains(listener)){
                mListeners.remove(listener);
            }
            else {
                Log.i(TAG, "不存在这个监听器");
            }
            Log.i(TAG, "监听器的个数:"+mListeners.size());
        }
    };
    public BookManagerServer() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mBookLists.add(new Book(1, "Android"));
        mBookLists.add(new Book(2, "IOS"));
        new Thread(new ServerWorker()).start();
    }


    private void onNewBookArrived(Book book )throws RemoteException{
        mBookLists.add(book);
        Log.d(TAG, "开始逐个进行监听器的开启:"+mListeners.size());
        for (IOnNewBookArrivedListener listener : mListeners) {
            Log.d(TAG, "告知监听器:"+listener);
            listener.onNewBookArrived(book);
        }
    }

    /**
     * 每隔5s添加一本书籍
     */
    private class ServerWorker implements Runnable{
        @Override
        public void run() {
            while(!isServiceDestroy.get()){
                try{
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int bookId = mBookLists.size()+1;
                Book newBook = new Book(bookId, "new Book# "+bookId);
                try{
                    onNewBookArrived(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

        }
    }

}
  1. 客户端活动
/**
 * @author xuanqis
 * 客户端活动,能够增加书籍,查询书籍列表,在新书到达时接受书籍信息
 */
public class BookManagerActivity extends AppCompatActivity {

    private static final String TAG = "BookManagerActivity";
    //新书到达的消息what
    private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;

    private IBookManager bookManager;
    /**
     * 在主线程处理消息
     */
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_NEW_BOOK_ARRIVED:
                    Log.d(TAG, "新来了一本书:" + msg.obj);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    };
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            bookManager = IBookManager.Stub.asInterface(service);
            try {
                List<Book> list = bookManager.getBookList();
                Log.i(TAG, "query book list, list type:" + list.getClass());
                Log.i(TAG, "query book list, list type:" + list.toString());
                Book book = new Book(3, "Android进阶");
                bookManager.addBook(book);
                Log.d(TAG, "加入:" + book);
                list = bookManager.getBookList();
                Log.d(TAG, "书单:" + list.toString());
                bookManager.registerListener(mIOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            bookManager = null;
            Log.e(TAG, "连接关闭.");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_book_manager);
        //绑定客户端和服务端
        Intent intent = new Intent(this, BookManagerServer.class);
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        //这里理论上个取消注册,但是实际上传到服务端的跟原先生成的不是一个对象,所以不可能
        //通过remove来取消注册
        if (bookManager != null && bookManager.asBinder().isBinderAlive()) {
            try {
                bookManager.unRegisterListener(mIOnNewBookArrivedListener);
                Log.d(TAG, "解除注册" + bookManager);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        //解除绑定
        unbindService(mServiceConnection);
        super.onDestroy();
    }


    /**
     * 新书到达的监听器,在客户端进程执行
     */
    private IOnNewBookArrivedListener mIOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
        @Override
        public void onNewBookArrived(Book book) throws RemoteException {
            //这里的book是放在obj里面,要注意的是obj的跨进程只能支持系统设置的Parcelable类
            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, book).sendToTarget();
        }
    };

}
  1. aidl
// Book.aidl
package com.example.a2.aidl;

parcelable Book;

// IBookManager.aidl
package com.example.a2.aidl;

import com.example.a2.aidl.Book;
import com.example.a2.aidl.IOnNewBookArrivedListener;
interface IBookManager {
    List<Book> getBookList();

    void addBook(in Book book);

    void registerListener(IOnNewBookArrivedListener listener);
    void unRegisterListener(IOnNewBookArrivedListener listener);
}


// IOnNewBookArrivedListener.aidl
package com.example.a2.aidl;

import com.example.a2.aidl.Book;

interface IOnNewBookArrivedListener {
    void onNewBookArrived(in Book book);
}
发表在 Android | 留下评论

可重入自旋锁实现

import java.util.concurrent.atomic.AtomicReference;

public class Test {

    public static void main(String[] args) throws InterruptedException {
        SpinLock lock = new SpinLock();
        MyRunnable runnable = new MyRunnable(lock);;
        for (int i = 0; i < 100; i++) {
            Thread t = new Thread(runnable);
            t.start();
        }
        if (Thread.activeCount() > 2){
            Thread.yield();
        }
        if (runnable!=null){
            System.out.println(runnable.sum);
        }
    }
}

class MyRunnable implements Runnable{
    int sum;
    /**
     * 将锁放在创建线程的Runnable对象里面
     */
    private SpinLock lock;

    public MyRunnable(SpinLock lock) {
        this.lock = lock;
        sum=0;
    }

    @Override
    public void run() {
        this.lock.lock();
        this.lock.lock();
        sum++;
        this.lock.unLock();
        this.lock.unLock();
    }
}


/**
 * 可重入的自旋锁  (重新进入 不会出现死锁)
 */
class SpinLock {
    //持有自旋锁的线程对象 
    AtomicReference<Thread> owner = new AtomicReference<>();
    //用一个计数器 来做 重入锁获取次数的计数
    private int count;

    public void lock() {
        Thread cur = Thread.currentThread();
        //同一线程可以多次加锁
        if (cur == owner.get()) {
            count++;
            return;
        }
        //这里耗时很严重
        while (!owner.compareAndSet(null, cur)) {
        }
    }

    public void unLock() {
        Thread cur = Thread.currentThread();
        if (cur == owner.get()) {
            if (count > 0) {
                count--;
            } else {
                owner.compareAndSet(cur, null);
            }
        }
    }
}
发表在 java | 留下评论

binder示例

  1. 终于弄懂啦原理和具体怎么样的过程,看这篇就够了,传送门
    在这里Binder的实例是BookManager的实现

  2. 首先是实现了Parcelable接口的Book类

/**
 * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com)
 * 2017/10/17
 * 书籍类,价格和名称
 */
public class Book implements Parcelable {

    private int price;
    private String name;

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.price);
        dest.writeString(this.name);
    }

    public Book() {
    }

    protected Book(Parcel in) {
        this.price = in.readInt();
        this.name = in.readString();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    @Override
    public String toString() {
        return "Book{" +
                "price=" + price +
                ", name='" + name + '\'' +
                '}';
    }
}
  1. 然后是定义了功能的BookManager接口
/**
 * 这个类用来定义服务端 RemoteService 具备什么样的能力
 *
 * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com)
 * 05/01/2018
 * 里面是放了两个方法
 */
public interface BookManager extends IInterface {

    List<Book> getBooks() throws RemoteException;

    void addBook(Book book) throws RemoteException;
}
  1. 然后是实现BookManager的Stub抽象类,在这个类里将客户端的数据进行进行反序列化,并进一步的调用方法。

/** * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) * 05/01/2018 * 处理Binder客户端请求的抽象类,进行反序列化,不进行对 反序列化的对象 执行具体的操作 * 处理对客户端的结果返回 */ public abstract class Stub extends Binder implements BookManager { private static final String DESCRIPTOR = "com.baronzhang.ipc.server.BookManager"; public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * 根据binder,返回对应的能供客户端调用的对象 * * @param binder 连接对应的binder * @return binder对应的客户端使用的对象 */ public static BookManager asInterface(IBinder binder) { //传入空,返回空的 if (binder == null) { return null; } //先找一下是不是同一进程的对象,是的话就直接返回对应的对象,不是的话就返回代理 //这里是客户进程进行访问 IInterface iin = binder.queryLocalInterface(DESCRIPTOR); if (iin != null && iin instanceof BookManager) { return (BookManager) iin; } return new Proxy(binder); } @Override public IBinder asBinder() { return this; } @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { switch (code) { case INTERFACE_TRANSACTION: reply.writeString(DESCRIPTOR); return true; //得到书籍的List case TRANSAVTION_getBooks: data.enforceInterface(DESCRIPTOR); List<Book> result = this.getBooks(); reply.writeNoException(); reply.writeTypedList(result); return true; //添加一本书,首先将书籍反序列化,然后调用添加方法 case TRANSAVTION_addBook: data.enforceInterface(DESCRIPTOR); Book arg0 = null; if (data.readInt() != 0) { arg0 = Book.CREATOR.createFromParcel(data); } this.addBook(arg0); reply.writeNoException(); return true; default: break; } return super.onTransact(code, data, reply, flags); } public static final int TRANSAVTION_getBooks = IBinder.FIRST_CALL_TRANSACTION; public static final int TRANSAVTION_addBook = IBinder.FIRST_CALL_TRANSACTION + 1; }
  1. 然后是服务service,这个定义在单独的进程中,里面有Stub的对象,这个对象其实就是整个应用能够进程通信的Binder对象。
/**
 * 返回binder,提供服务
 */
public class RemoteService extends Service {

    private List<Book> books = new ArrayList<>();

    public RemoteService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Book book = new Book();
        book.setName("三体");
        book.setPrice(88);
        books.add(book);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return bookManager;
    }

    /**
     * 具体的服务端对客户端传过来的数据进行处理的对象,实现了抽象类未实现的具体方法逻辑
     */
    private final Stub bookManager = new Stub() {
        @Override
        public List<Book> getBooks() throws RemoteException {
            synchronized (this) {
                if (books != null) {
                    return books;
                }
                return new ArrayList<>();
            }
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            synchronized (this) {
                if (books == null) {
                    books = new ArrayList<>();
                }

                if (book == null) {
                    return;
                }

                book.setPrice(book.getPrice() * 2);
                books.add(book);

                Log.e("Server", "books: " + book.toString());
            }
        }
    };
}
  1. 客户端进程调用Stub的方法得到BookManager的实例,但是其实是得到的代理,这个代理中有Binder的引用,当调用这个Binder的方法,实际上调用的是注册在ServiceManager里面的Binder实例的实际对象方法。

/** * @author baronzhang (baron[dot]zhanglei[at]gmail[dot]com) * 05/01/2018 * 客户端的代理类,将数据进行序列话,按格式传给服务端 */ public class Proxy implements BookManager { private static final String DESCRIPTOR = "com.baronzhang.ipc.server.BookManager"; private IBinder remote; public Proxy(IBinder remote) { this.remote = remote; } public String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public List<Book> getBooks() throws RemoteException { Parcel data = Parcel.obtain(); Parcel replay = Parcel.obtain(); List<Book> result; try { //加个token data.writeInterfaceToken(DESCRIPTOR); //调用服务端,将序列化的数据传进去,这里是没有数据要传 remote.transact(Stub.TRANSAVTION_getBooks, data, replay, 0); replay.readException(); result = replay.createTypedArrayList(Book.CREATOR); } finally { replay.recycle(); data.recycle(); } return result; } @Override public void addBook(Book book) throws RemoteException { Parcel data = Parcel.obtain(); Parcel replay = Parcel.obtain(); try { data.writeInterfaceToken(DESCRIPTOR); if (book != null) { data.writeInt(1); //将book写入到data中 book.writeToParcel(data, 0); } else { data.writeInt(0); } remote.transact(Stub.TRANSAVTION_addBook, data, replay, 0); replay.readException(); } finally { replay.recycle(); data.recycle(); } } @Override public IBinder asBinder() { return remote; } }
  1. 客户端通过绑定得到的Binder来创建代理,对代理的操作就是对服务端BookManager的操作。

/** * 客户端活动 */ public class ClientActivity extends AppCompatActivity { private BookManager bookManager; private boolean isConnection = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_client); Button btn = findViewById(R.id.btn); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //没绑定,进行绑定 if (!isConnection) { attemptToBindService(); return; } //出错啦,直接是空 if (bookManager == null) { return; } try { Book book = new Book(); book.setPrice(101); book.setName("编码"); bookManager.addBook(book); Log.d("ClientActivity", bookManager.getBooks().toString()); } catch (RemoteException e) { e.printStackTrace(); } } }); } /** * 绑定服务 */ private void attemptToBindService() { Intent intent = new Intent(this, RemoteService.class); intent.setAction("com.baronzhang.ipc.server"); bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE); } private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { isConnection = true; //返回供客户端使用的对象,同进程就直接是服务中的对象,不同进程是个代理 bookManager = Stub.asInterface(service); if (bookManager != null) { try { List<Book> books = bookManager.getBooks(); Log.d("ClientActivity", books.toString()); } catch (RemoteException e) { e.printStackTrace(); } } } @Override public void onServiceDisconnected(ComponentName name) { isConnection = false; } }; @Override protected void onStart() { super.onStart(); if (!isConnection) { attemptToBindService(); } } @Override protected void onStop() { super.onStop(); if (isConnection) { unbindService(serviceConnection); } } }
发表在 Android | 留下评论