Android 中的Java跟C/C++的Binder通信

1、大家都知道Android中進程間的通信是通過binder來實現的,這裏主要是講代碼中的簡單實現。如果想了解binder的通信細節或實現原理,可以參考https://blog.csdn.net/jmq_0000/article/details/7349844。個人覺得這篇博客講的還是很不錯的。現在就開始demo的介紹,該demo主要是通過java傳遞文件路徑給C++來實現刪除該路徑,目的是爲了瞭解java跟C++是怎麼通過binder來通信的。首先是介紹C++端的代碼,目錄結構如下:

主要是四個文件Android.mk、DeleteFile.cpp、DeleteFile.h、DeleteFileService.cpp。先看一下頭文件DeleteFile.h的裏面的內容 

 1 #ifndef _DELETE_FILE
  2 #define _DELETE_FILE
  3
  4 #include <utils/RefBase.h>
  5 #include <binder/IInterface.h>
  6 #include <binder/Parcel.h>
  7 #include <utils/threads.h>
  8 #include <stdio.h>
  9 #include <stdbool.h>
 10 #include <android/log.h>
 11
 12 #define LOG_TAG "JPEG_JNI"
 13 #define DEBUG
 14 #define ANDROID_PLATFORM
 15
 16 #ifdef DEBUG
 17         #ifdef ANDROID_PLATFORM
 18                 #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
 19                 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
 20                 #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__))
 21                 #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
 22
 23
 24         #else
 25                 #define LOGD(fmt, ...) printf(fmt"\n", ##__VA_ARGS__)
 26                 #define LOGI(fmt, ...) printf(fmt"\n", ##__VA_ARGS__)
 27                 #define LOGW(fmt, ...) printf(fmt"\n", ##__VA_ARGS__)
 28                 #define LOGE(fmt, ...) printf(fmt"\n", ##__VA_ARGS__)
 29         #endif
 30 #else
 31         #define LOGD(...);
 32         #define LOGI(...);
 33         #define LOGW(...);
 34         #define LOGE(...);
 35 #endif
 36 38 namespace android{
 39         class DeleteFile: public BBinder{
 40         public:
 41                 static int instantiate();
 42                 DeleteFile();
 43                 virtual ~DeleteFile();
 44                 virtual status_t onTransact(uint32_t, const Parcel&, Parcel*, uint32_t);
 45         };
 46 }//namespace
 47
 48 #endif
 37
 頭文件裏面是主要定義Android的LOG打印及刪除文件DeleteFile類的定義及方法聲明。DeleteFile類中有四個方法,主要用的是instantiate()跟onTransact()的方法。看一下這兩個方法的實現

27 int DeleteFile::instantiate(){
 28         int r = defaultServiceManager()->addService(String16(SERVICE_NAME), new DeleteFile());
 29         return r;
 30 }
 31
 32 status_t DeleteFile::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
 33                 uint32_t flags) {
 34         switch (code) {
 35         case DELETE_FILE:{
 36 //              int keep = data.readInt32();
 37                 String16 path = data.readString16();
 38                 LOGD("[%s(L:%d)] path = %s\n", __FUNCTION__, __LINE__, String8(path).string());
 39                 int result = dealDeleteFiles(String8(path).string());
 40                 reply->writeInt32(result);
 41                 system("sync");
 42                 return 0;
 43         }
 44         break;
 45         default: {
 46                         return BBinder::onTransact(code, data, reply, flags);
 47                 }
 48                         break;
 49         }
 50 }
instantiate() 方法主要是向defaultServiceManager中添加自己定義的服務,服務的名稱是chinatsp.autoaction。而onTransact()就是真正通信的方法,在服務啓動的情況下,C++中的onTransact()方法跟Java中的binder.transact(cmd, data, reply, 0)進行通信的。方法中code跟data是java中的transact裏面的cmd跟data傳遞過來的,dealDeleteFiles(String8(path).string())方法是實現刪除java端傳遞過來的指定目錄的,它的實現非常簡單,就是通過rm -rf path 命令刪除目錄。

12 int dealDeleteFiles(const char* path){
 13         char* cmd = (char*) malloc(16 + strlen(path));
 14         memset(cmd, 0, sizeof(cmd));
 15         sprintf(cmd, "rm -rf %s", path);
 16         int result = system(cmd);
 17         free(cmd);
 18         return result;
 19 }
DeleteFile.cpp完整代碼如下:

1 #include "DeleteFile.h"
  2 #include <binder/IServiceManager.h>
  3 #include <binder/IPCThreadState.h>
  4 #include <cutils/log.h>
  5 #include <sys/types.h>
  6 #include <sys/stat.h>
  7 #include <fcntl.h>
  8
  9 #define DELETE_FILE 0x01
 10 #define SERVICE_NAME "chinatsp.autoaction"
 11
 12 int dealDeleteFiles(const char* path){
 13         char* cmd = (char*) malloc(16 + strlen(path));
 14         memset(cmd, 0, sizeof(cmd));
 15         sprintf(cmd, "rm -rf %s", path);
 16         int result = system(cmd);
 17         free(cmd);
 18         return result;
 19 }
 20
 21 namespace android{
 22 DeleteFile::DeleteFile(){
 23 }
 24 DeleteFile::~DeleteFile(){
 25
 26 }
 27 int DeleteFile::instantiate(){
 28         int r = defaultServiceManager()->addService(String16(SERVICE_NAME), new DeleteFile());
 29         return r;
 30 }
 31
32 status_t DeleteFile::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
 33                 uint32_t flags) {
 34         switch (code) {
 35         case DELETE_FILE:{
 36 //              int keep = data.readInt32();
 37                 String16 path = data.readString16();
 38                 LOGD("[%s(L:%d)] path = %s\n", __FUNCTION__, __LINE__, String8(path).string());
 39                 int result = dealDeleteFiles(String8(path).string());
 40                 reply->writeInt32(result);
 41                 system("sync");
 42                 return 0;
 43         }
 44         break;
 45         default: {
 46                         return BBinder::onTransact(code, data, reply, flags);
 47                 }
 48                         break;
 49         }
 50 }
 51
 52 }//namespace
接下來再看一下DeleteFileService.cpp的代碼實現:

 1 #include <sys/types.h>
  2 #include <unistd.h>
  3 #include <grp.h>
  4 #include <binder/IPCThreadState.h>
  5 #include <binder/ProcessState.h>
  6 #include <binder/IServiceManager.h>
  7 #include <private/android_filesystem_config.h>
  8 #include "DeleteFile.h"
  9
 10 using namespace android;
 11 int main(int argc, char** argv)
 12 {
 13         LOGD("main begin.....");
 14         LOGD("[%s(L:%d)] \n", __FUNCTION__, __LINE__);
 15         sp<ProcessState> proc(ProcessState::self());
 16         sp<IServiceManager> sm = defaultServiceManager(); //取得 ServiceManager
 17         DeleteFile::instantiate();
 18         ProcessState::self()->startThreadPool(); //啓動緩衝池
 19         IPCThreadState::self()->joinThreadPool(); //這裏是把服務添加到 Binder閉合循環進程中
 20         LOGD("main end.....");
 21 }
可以看到main函數裏面只有5行的關鍵代碼,15-19中只有17行是自己定義的方法,其他都是binder綁定服務需要調用的。最後一個文件Android.mk也很簡單 ,就不做介紹了,代碼如下:

 1 LOCAL_PATH:= $(call my-dir)
  2
  3 include $(CLEAR_VARS)
  4
  5 LOCAL_SRC_FILES:= DeleteFile.cpp DeleteFileService.cpp
  6 LOCAL_C_INCLUDES := \
  7         DeleteFile.h \
  8         $(JNI_H_INCLUDE)
  9 LOCAL_SHARED_LIBRARIES := \
 10         liblog \
 11         libutils \
 12         libandroid_runtime \
 13         libbinder \
 14         libcutils
 15 LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
 16 LOCAL_MODULE:= tspautoservice
 17 include $(BUILD_EXECUTABLE)
到這裏C++端的代碼就介紹完了,接下在就是Java端的代碼了,主要涉及兩個java類及一個佈局文件:

java端的界面佈局backup_log_fragment.xml很簡單,只有一個Button。

<?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:orientation="vertical" >
    <Button 
        android:id="@+id/delelte_log"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/delete_log"/>
 
</LinearLayout>
BackupLogsFragment.java類主要是點擊事件及 DeleteLogsTask異步實現刪除目錄路徑傳遞,這裏要刪除的是/data/media目錄(data目錄下的medai目錄)

package com.gunder.tool.fragment;
 
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;
 
import com.gunder.tool.R;
import com.gunder.tool.utils.Logger;
import com.gunder.tool.utils.TLogUtils;
 
public class BackupLogsFragment extends Fragment implements OnClickListener{
    Button deleteLog;
    @Override
    public View onCreateView(LayoutInflater inflater,
             ViewGroup container,  Bundle savedInstanceState) {
        return inflater.inflate(R.layout.backup_log_fragment, container, false);
    }
    
    @Override
    public void onViewCreated(View view,  Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        
        initView(view);
        
    }
 
    private void initView(View root) {
        deleteLog = (Button) root.findViewById(R.id.delelte_log);
        deleteLog.setOnClickListener(this);
    }
 
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.delelte_log:
            deleteLogs();
            break;
 
        default:
            break;
        }
    }
    
    //開始刪除日誌
    private void deleteLogs(){
         final String delete_path = "/data/media";
         new DeleteLogsTask().execute(delete_path);
    }
    
    private class DeleteLogsTask extends AsyncTask<String, String, String>{
        
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            Toast.makeText(getActivity(), "delete file ...", Toast.LENGTH_LONG).show();
        }
 
        @Override
        protected String doInBackground(String... params) {
            boolean result = true;
            String path = params[0];
            Logger.d("path = " + path);
            boolean ret = deleteLog(path);
            result = result && ret;
            return result ? "success" : "failed";
        }
        protected boolean deleteLog(String path){
            Logger.d();
            return TLogUtils.deleteLog(path);
        }
        
        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
            Toast.makeText(getActivity(), "delete " + result, Toast.LENGTH_LONG).show();
        }
    }
}
 BackupLogsFragment.java類中的實行流程大概如下onClick-->deleteLogs-->doInBackground-->deleteLog-->TLogUtils.deleteLog,發現最後會執行TLogUtils類中的deleteLog靜態方法,現在來看一下TLogUtils類中的代碼:

package com.gunder.tool.utils;
 
import java.io.File;
 
import android.os.IBinder;
import android.os.Parcel;
import android.os.ServiceManager;
import android.os.SystemProperties;
 
public class TLogUtils {
    //向C++中傳遞cmd命令,C++對應的參數是code
    private final static int DELETE_FILES = 0x1;
    //如果文件存在,就執行deleteLog方法
    public static boolean deleteLog(String src){
        Logger.d("src = " + src);
        if (null == src){
            return false;
        }
        String path = src;
        while ((!path.isEmpty()) && path.charAt(path.length() - 1) == '/')
        {
            path = path.substring(0, path.length() - 1);
        }
        path = path.trim();
        Logger.d("path = " + path);
        if (path.isEmpty()){
            Logger.d("path is empty");
            return true;
        }
        boolean result = false;
        File file = new File(path);
        if (file.exists()){
            result = deleteLogFiles(file);
        }else {
            result = true;
        }
        if (!result){
            Logger.d("delete log failed: " + src);
        }
        return result;
    }
 
    //如果文件是目錄,執行deleteLogFilesUseRoot,否則直接file.delete()
    private static boolean deleteLogFiles(File file) {
        Logger.d();
        if (file != null && file.exists()){
            Logger.d();
            if (file.isDirectory()){
                Logger.d();
                return deleteLogFilesUseRoot(file.getAbsolutePath());
            }
        }else {
            Logger.d();
            return file.delete();
        }
        return true;
    }
 
    //對要刪除的路徑進行Parcel寫入
    private static boolean deleteLogFilesUseRoot(String absolutePath) {
        Logger.d("absolutePath = " + absolutePath);
        Parcel data = Parcel.obtain();
        data.writeString(absolutePath);
        return dealUseRoot(DELETE_FILES, data);
    }
 
    /**
     * 啓動chinatsp.autoaction服務,並且調用binder.transact向C++中的onTransact進行通信
     * @param cmd 傳遞命令,這裏是DELETE_FILES = 0x1,跟C++端的定義是一樣的
     * @param data 傳遞路徑,這裏是/data/media
     * @return
     */
    private static boolean dealUseRoot(int cmd, Parcel data) {
        IBinder binder = null;
        final int MAX_WAIT = 9; //最多獲取service 10次
        int index = 0;
        SystemProperties.set("ctl.start", "tsp_auto_service");
        do {
            Logger.d("get service : chinatsp.autoaction");
            binder = ServiceManager.getService("chinatsp.autoaction");
            if (null == binder){
                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }while ((null == binder) && ((index++) < MAX_WAIT));
        if (null == binder){
            Logger.d("get service of autoaction failed");
            data.recycle();
            SystemProperties.set("ctl.stop", "tsp_auto_service");
            return false;
        }
        Parcel reply = Parcel.obtain();
        int result = -1;
        try {
            Logger.d("cmd = " + cmd);
            binder.transact(cmd, data, reply, 0);
            result = reply.readInt();
        } catch (Exception e) {
            Logger.d("send cmd to autoaction service : error");
        }
        data.recycle();
        reply.recycle();
        SystemProperties.set("ctl.stop", "tsp_auto_service");
        return result == 0;
    }
}
 TLogUtils類中實行流程是這樣的:deleteLog-->deleteLogFiles-->deleteLogFilesUseRoot-->dealUseRoot。具體代碼相信大家可以看懂。最後需要在init.rc中配置一下自定義的服務chinatsp.autoaction的對應的可執行文件tspautoservice,不然調用SystemProperties.set("ctl.start", "tsp_auto_service")不會啓動服務。init.rc配置如下:

245 service tsp_auto_service /system/bin/tspautoservice
246     class main
247     disabled
好了,到這裏就介紹完了。

項目的編譯可以參考:https://blog.csdn.net/u013357557/article/details/81411686

完整的代碼路徑:https://github.com/gunder1129/android-tool/tree/master/ToolTemplate 
--------------------- 
作者:Gunder 
來源:CSDN 
原文:https://blog.csdn.net/u013357557/article/details/81482729 
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章