美文网首页
Android 进程间通信

Android 进程间通信

作者: 放羊娃华振 | 来源:发表于2019-04-17 20:35 被阅读0次

一、概述
IPC是Inter-Process Communication的缩写,含义就是进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程。那么什么是进程,什么是线程,进程和线程是两个截然不同的概念。在操作系统中,线程是CPU调度的最小单元,同时线程是一种有限的系统资源。而进程指的一个执行单元,在PC和移动设备上指的是一个程序或者一个应用。一个进程可以包含多个线程,因此进程和线程是包含被包含的关系,最简单情况下,一个进程可以只有一个线程,即主线程,在Android里面也叫UI线程,在UI线程里才能操作界面元素。
AIDL 意思即 Android Interface Definition Language,翻译过来就是Android接口定义语言,是用于定义服务器和客户端通信接口的一种描述语言,可以拿来生成用于IPC的代码。从某种意义上说AIDL其实是一个模板,因为在使用过程中,实际起作用的并不是AIDL文件,而是据此而生成的一个IInterface的实例代码,AIDL其实是为了避免我们重复编写代码而出现的一个模板
设计AIDL这门语言的目的就是为了实现进程间通信。在Android系统中,每个进程都运行在一块独立的内存中,在其中完成自己的各项活动,与其他进程都分隔开来。可是有时候我们又有应用间进行互动的需求,比较传递数据或者任务委托等,AIDL就是为了满足这种需求而诞生的。通过AIDL,可以在一个进程中获取另一个进程的数据和调用其暴露出来的方法,从而满足进程间通信的需求
通常,暴露方法给其他应用进行调用的应用称为服务端,调用其他应用的方法的应用称为客户端,客户端通过绑定服务端的Service来进行交互

二、Demo功能和介绍
体验功能请安装demo目录下apk文件,一个为Server端,一个是client端。实现client从server端获取用户信息、重置用户信息和修改用户信息的功能。

三、服务器端实现
1、新建User.java实体类(java目录下com.ljd.aidl.entity.User):

/**
 * 用户实体类
 */
public class User implements Parcelable {

    private String uid;
    private String uname;

    protected User(Parcel in) {
        uid = in.readString();
        uname = in.readString();
    }

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

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

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(uid);
        dest.writeString(uname);
    }

    /**
     * 自己定义的readFromParcel方法, 这样才支持 定向tag out
     *
     * @param dest
     */
    public void readFromParcel(Parcel dest) {
        //注意,此处的读值顺序应当是和writeToParcel()方法中一致的
        uid = dest.readString();
        uname = dest.readString();
    }

    @Override
    public String toString() {
        return String.format("[uid:%s, uname:%s]", uid, uname);
    }
}

2、生成User.aidl文件(aidl目录下com.ljd.aidl.entity)

package com.ljd.aidl.entity;
//aidl的user声明java中的user对象
parcelable User;

3、生成IUserManager.aidl文件(aidl目录下com.ljd.aidl)

package com.ljd.aidl;

//导入所需要使用的非默认支持数据类型的包, 和java类似
import com.ljd.aidl.entity.User;
//接口定义语言, 当然要定义接口啦, 和java接口类似
interface IUserManager {

    //所有的返回值前都不需要加任何东西,不管是什么数据类型
    User getUser();

    //传参时除了Java基本类型以及String,CharSequence之外的类型
    //都需要在前面加上定向tag,具体加什么量需而定
    void setUser(in User user);

    //修改用户的姓名
    void setUserName(String name);

}

4、生成UserService.java文件(java目录下)

package com.ljd.aidl.service;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;

import com.ljd.aidl.IUserManager;
import com.ljd.aidl.entity.User;

public class UserService extends Service {

    private User mUser;

    /**
     * 根据AIDL文件 编译器为我们生成的BookManager
     */
    private final IUserManager.Stub mUserManager = new IUserManager.Stub() {


        @Override
        public User getUser() throws RemoteException {
            return mUser;
        }

        @Override
        public void setUser(User user) throws RemoteException {
            mUser = user;
        }

        @Override
        public void setUserName(String name) throws RemoteException {
            if (mUser != null) {
                mUser.setUname(name);
            }
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        User user = new User();
        user.setUid("10000");
        user.setUname("测试10000");
        try {
            if (mUserManager != null) {
                mUserManager.setUser(user);
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }

    }

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

四、客户端实现
1、拷贝aidl路径下的所有aidl文件到client目录下(保持包名不变)。
2、拷贝java路径下的用到Entity实体类到client目录下(保持包名不变)。
3、编写调用代码:

package com.ljd.aidl.activity;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import com.ljd.aidl.IUserManager;
import com.ljd.aidl.client.R;
import com.ljd.aidl.entity.Book;
import com.ljd.aidl.entity.User;

import butterknife.ButterKnife;
import butterknife.OnClick;

public class Demo5Activity extends AppCompatActivity {

    private final String TAG = "DEMO5";
    private boolean mIsBindService;
    private IUserManager mIUserManager;

    private TextView TvContent;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo5);
        TvContent = (TextView) findViewById(R.id.TvContent);
        ButterKnife.bind(this);
    }

    @Override
    protected void onDestroy() {
        unbindService();
        ButterKnife.unbind(this);
        super.onDestroy();
    }

    @OnClick({R.id.bind_demo4_btn, R.id.unbind_demo4_btn, R.id.get_btn, R.id.set_btn,R.id.update_btn})
    public void onClickButton(View v) {
        switch (v.getId()) {
            case R.id.bind_demo4_btn:
                bindService();
                TvContent.setText("已绑定");
                break;
            case R.id.unbind_demo4_btn:
                Toast.makeText(this, "unbind service success", Toast.LENGTH_SHORT).show();
                unbindService();
                TvContent.setText("未绑定");
                break;
            case R.id.get_btn:
                getData();
                break;
            case R.id.set_btn:
                User suser = new User();
                suser.setUname("uname from  client");
                suser.setUid("20000");
                try {
                    mIUserManager.setUser(suser);
                    TvContent.setText("设置数据成功:"+suser.toString());
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            case R.id.update_btn:

                try {
                    mIUserManager.setUserName("is update name");
                    TvContent.setText("修改用名称为:is update name");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
        }
    }

    private void getData() {

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                User user = null;
                try {
                    user = mIUserManager.getUser();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                TvContent.setText(user.toString());
            }
        });

    }

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG, "bind success");
            Toast.makeText(Demo5Activity.this, "bind service success", Toast.LENGTH_SHORT).show();
            mIUserManager = IUserManager.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            //重新绑定Service防止系统将服务进程杀死而产生的调用错误。
            bindService();
        }
    };

    private void bindService() {
        Intent intent = new Intent();
        intent.setAction("com.ljd.aidl.action.USER_SERVICE");
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
        mIsBindService = true;
    }

    private void unbindService() {
        if (mIsBindService) {
            mIsBindService = false;
            unbindService(mConnection);
        }
    }
}

五、优化方案以及封装成sdk
通过上面的编码可以实现不同的apk的进程间通信问题,但是使用中还需要有统一的标准,否则就会导致重复的工作和一些未知的Bug。我建议的优化方案是,由Server端封装代码,把两端都封装成lib库,当有别的apk需要用的时候,直接调用lib里面的方法,这样就可以很好的维护代码和节省人力成本。

相关文章

网友评论

      本文标题:Android 进程间通信

      本文链接:https://www.haomeiwen.com/subject/nryowqtx.html