開年第一篇賤賤的Android NDK服務

首先先祝大家新年快樂,晚了一點哈,寫這篇文章主要是因爲項目中需要,我暫時性的做一個記錄,首先要感謝LeBron_Six 這位博主對文章中技術的支持;Google 對安卓性能上的優化那也不是蓋得,安卓5.0的出現已經表現出了很多,然後現在又是6.0和7.0,之前做Service所用用到的是在OnStartCommand中返回START_STICKY;然而呢,這個現在看起來並不好用,然後網上看文章,找資料,發現有解決方案,真的不錯,哈哈,幫助了我很大,希望這個文章寫出來能幫助你;同時這也是我對上半年的一個總結吧;上年中沒更新多少,希望在今年中能給大家帶來更多更好的文章吧,我一個新手的爬坑之路,希望大家能夠諒解,有些問題不明白的希望能和大家一起討論完成;廢話不多說了,上幹活吧。(本文中依然用as進行開發,至於爲什麼沒用Cmake。主要是我覺得還是這個比較好,用着比較舒服習慣吧)

一.工程目錄結構

這裏寫圖片描述

二.主要的native代碼

package lv.anto.com.ndktest;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;

public class NativeRuntime {

    private static NativeRuntime theInstance = null;  

    private NativeRuntime() {  

    }  

    public static NativeRuntime getInstance() {  
        if (theInstance == null)  
            theInstance = new NativeRuntime();  
        return theInstance;  
    }  

    /** 
     * RunExecutable 啓動一個可自行的lib*.so文件 
     * @param pacaageName
     * @param filename 
     * @param alias 別名 
     * @param args 參數 
     * @return 
     */  
    public String RunExecutable(String pacaageName, String filename, String alias, String args) {  
        String path = "/data/data/" + pacaageName;  
        String cmd1 = path + "/lib/" + filename;  
        String cmd2 = path + "/" + alias;  
        String cmd2_a1 = path + "/" + alias + " " + args;  
        String cmd3 = "chmod 777 " + cmd2;  
        String cmd4 = "dd if=" + cmd1 + " of=" + cmd2;  
        StringBuffer sb_result = new StringBuffer();  

        if (!new File("/data/data/" + alias).exists()) {
            RunLocalUserCommand(pacaageName, cmd4, sb_result); // 拷貝lib/libtest.so到上一層目錄,同時命名爲test.  
            sb_result.append(";");  
        }  
        RunLocalUserCommand(pacaageName, cmd3, sb_result); // 改變test的屬性,讓其變爲可執行  
        sb_result.append(";");  
        RunLocalUserCommand(pacaageName, cmd2_a1, sb_result); // 執行test程序.  
        sb_result.append(";");  
        return sb_result.toString();  
    }  

    /** 
     * 執行本地用戶命令 
     * @param pacaageName
     * @param command 
     * @param sb_out_Result 
     * @return 
     */  
    public boolean RunLocalUserCommand(String pacaageName, String command, StringBuffer sb_out_Result) {  
        Process process = null;  
        try {  
            process = Runtime.getRuntime().exec("sh"); // 獲得shell進程  
            DataInputStream inputStream = new DataInputStream(process.getInputStream());
            DataOutputStream outputStream = new DataOutputStream(process.getOutputStream());
            outputStream.writeBytes("cd /data/data/" + pacaageName + "\n"); // 保證在command在自己的數據目錄裏執行,纔有權限寫文件到當前目錄  
            outputStream.writeBytes(command + " &\n"); // 讓程序在後臺運行,前臺馬上返回  
            outputStream.writeBytes("exit\n");  
            outputStream.flush();  
            process.waitFor();  
            byte[] buffer = new byte[inputStream.available()];  
            inputStream.read(buffer);  
            String s = new String(buffer);  
            if (sb_out_Result != null)  
                sb_out_Result.append("CMD Result:\n" + s);  
        } catch (Exception e) {  
             if (sb_out_Result != null)
                sb_out_Result.append("Exception:" + e.getMessage());  
            return false;  
        }  
        return true;  
    }  

    public native void startActivity(String compname);

    public native String stringFromJNI();

    public native void startService(String srvname, String sdpath);

    public native int findProcess(String packname);

    public native int stopService();  

    static {  
        try {  
            System.loadLibrary("NativeRuntime"); // 加載so庫
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  

}

三.用javah生成編譯native 出來的是NativeRuntime.h文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class lv_anto_com_ndktest_NativeRuntime */

#ifndef _Included_lv_anto_com_ndktest_NativeRuntime
#define _Included_lv_anto_com_ndktest_NativeRuntime
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     lv_anto_com_ndktest_NativeRuntime
 * Method:    startActivity
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_lv_anto_com_ndktest_NativeRuntime_startActivity
  (JNIEnv *, jobject, jstring);

/*
 * Class:     lv_anto_com_ndktest_NativeRuntime
 * Method:    stringFromJNI
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_lv_anto_com_ndktest_NativeRuntime_stringFromJNI
  (JNIEnv *, jobject);

/*
 * Class:     lv_anto_com_ndktest_NativeRuntime
 * Method:    startService
 * Signature: (Ljava/lang/String;Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_lv_anto_com_ndktest_NativeRuntime_startService
  (JNIEnv *, jobject, jstring, jstring);

/*
 * Class:     lv_anto_com_ndktest_NativeRuntime
 * Method:    findProcess
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_lv_anto_com_ndktest_NativeRuntime_findProcess
  (JNIEnv *, jobject, jstring);

/*
 * Class:     lv_anto_com_ndktest_NativeRuntime
 * Method:    stopService
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_lv_anto_com_ndktest_NativeRuntime_stopService
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

四.配置文件Android.mk 和Application.mk;

Android.mk

LOCAL_PATH := (callmydir)include (CLEAR_VARS)
LOCAL_MODULE := NativeRuntime
LOCAL_SRC_FILES := NativeRuntime.c
include $(BUILD_SHARED_LIBRARY)

Application.mk

APP_ABI := all

五.上面弄完了寫下.C文件的編寫了,主代碼來了,別眨眼啊;

//
// Created by mac on 16/12/30.
//
#include <string.h>
#include <jni.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#include <sys/resource.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>

#define PROC_DIRECTORY "/proc/"



void thread(char* srvname);

char* a;

/**
 * srvname  服務名
 * sd 之前創建子進程的pid寫入的文件路徑
 */
int start(int argc, char* srvname, char* sd) {
    pthread_t id;
    int ret;
    struct rlimit r;

    /**
     * 第一次fork的作用是讓shell認爲本條命令已經終止,不用掛在終端輸入上。
     * 還有一個作用是爲後面setsid服務。setsid的調用者不能是進程組組長(group leader)。
     * 此時父進程是進程組組長。
     */
    int pid = fork();
    if (pid < 0) {
        exit(0);
    } else if (pid != 0) {
        //exit(0);
    } else { //  第一個子進程
        //int setsid = setsid();
        umask(0); //使用umask修改文件的屏蔽字,爲文件賦予跟多的權限,因爲繼承來的文件可能某些權限被屏蔽,從而失去某些功能,如讀寫

        int pid = fork();
        if (pid == 0) { // 第二個子進程
            FILE  *fp;
            sprintf(sd,"%s/pid",sd);
            if((fp=fopen(sd,"a"))==NULL) {//打開文件 沒有就創建
                ftruncate(fp, 0);
                lseek(fp, 0, SEEK_SET);
            }
            fclose(fp);
            fp=fopen(sd,"rw");
            if(fp>0){
                char buff1[6];
                int p = 0;
                memset(buff1,0,sizeof(buff1));
                fseek(fp,0,SEEK_SET);
                fgets(buff1,6,fp);  //讀取一行
                if(strlen(buff1)>1){ // 有值

                    kill(atoi(buff1), SIGTERM);
                }
            }
            fclose(fp);
            fp=fopen(sd,"w");
            char buff[100];
            int k = 3;
            if(fp>0){
                sprintf(buff,"%lu",getpid());
                fprintf(fp,"%s\n",buff); // 把進程號寫入文件
            }
            fclose(fp);
            fflush(fp);

            //step 4:修改進程工作目錄爲根目錄,chdir(“/”).
            chdir("/");
            //step 5:關閉不需要的從父進程繼承過來的文件描述符。
            if (r.rlim_max == RLIM_INFINITY) {
                r.rlim_max = 1024;
            }
            int i;
            for (i = 0; i < r.rlim_max; i++) {
                close(i);
            }

            umask(0);
            ret = pthread_create(&id, NULL, (void *) thread, srvname);
            if (ret != 0) {
                printf("Create pthread error!\n");
                exit(1);
            }
            int stdfd = open ("/dev/null", O_RDWR);
            dup2(stdfd, STDOUT_FILENO);
            dup2(stdfd, STDERR_FILENO);
        } else {
            exit(0);
        }
    }
    return 0;
}
/**
 * 執行命令
 */
void ExecuteCommandWithPopen(char* command, char* out_result,
        int resultBufferSize) {
    FILE * fp;
    out_result[resultBufferSize - 1] = '\0';
    fp = popen(command, "r");
    if (fp) {
        fgets(out_result, resultBufferSize - 1, fp);
        out_result[resultBufferSize - 1] = '\0';
        pclose(fp);
    } else {
        exit(0);
    }
}

/**
 * 檢測服務,如果不存在服務則啓動.
 * 通過am命令啓動一個laucher服務,由laucher服務負責進行主服務的檢測,laucher服務在檢測後自動退出
 */
void check_and_restart_service(char* service) {
    char cmdline[200];
    sprintf(cmdline, "am startservice --user 0 -n %s", service);
    char tmp[200];
    sprintf(tmp, "cmd=%s", cmdline);
    ExecuteCommandWithPopen(cmdline, tmp, 200);
}

void thread(char* srvname) {
    while(1){
        check_and_restart_service(srvname);
        sleep(4);
    }
}


jstring stoJstring(JNIEnv* env, const char* pat) {
    jclass strClass = (*env)->FindClass(env, "Ljava/lang/String;");
    jmethodID ctorID = (*env)->GetMethodID(env, strClass, "<init>",
            "([BLjava/lang/String;)V");
    jbyteArray bytes = (*env)->NewByteArray(env, strlen(pat));
    (*env)->SetByteArrayRegion(env, bytes, 0, strlen(pat), (jbyte*) pat);
    jstring encoding = (*env)->NewStringUTF(env, "utf-8");
    return (jstring)(*env)->NewObject(env, strClass, ctorID, bytes, encoding);
}


/**
 * 判斷是否是數字
 */
int IsNumeric(const char* ccharptr_CharacterList) {
    for (; *ccharptr_CharacterList; ccharptr_CharacterList++)
        if (*ccharptr_CharacterList < '0' || *ccharptr_CharacterList > '9')
            return 0; // false
    return 1; // true
}


//intCaseSensitive=0大小寫不敏感
int strcmp_Wrapper(const char *s1, const char *s2, int intCaseSensitive) {
    if (intCaseSensitive)
        return !strcmp(s1, s2);
    else
        return !strcasecmp(s1, s2);
}


//intCaseSensitive=0大小寫不敏感
int strstr_Wrapper(const char* haystack, const char* needle,
        int intCaseSensitive) {
    if (intCaseSensitive)
        return (int) strstr(haystack, needle);
    else
        return (int) strcasestr(haystack, needle);
}



/**
 * 通過進程名稱獲取pid
 */
pid_t GetPIDbyName_implements(const char* cchrptr_ProcessName,
        int intCaseSensitiveness, int intExactMatch) {

    char chrarry_CommandLinePath[100];
    char chrarry_NameOfProcess[300];
    char* chrptr_StringToCompare = NULL;
    pid_t pid_ProcessIdentifier = (pid_t) - 1;
    struct dirent* de_DirEntity = NULL;
    DIR* dir_proc = NULL;

    int (*CompareFunction)(const char*, const char*, int);

    if (intExactMatch)
        CompareFunction = &strcmp_Wrapper;
    else
        CompareFunction = &strstr_Wrapper;

    dir_proc = opendir(PROC_DIRECTORY);
    if (dir_proc == NULL) {
        perror("Couldn't open the " PROC_DIRECTORY " directory");
        return (pid_t) - 2;
    }

    while ((de_DirEntity = readdir(dir_proc))) {
        if (de_DirEntity->d_type == DT_DIR) {

            if (IsNumeric(de_DirEntity->d_name)) {
                strcpy(chrarry_CommandLinePath, PROC_DIRECTORY);
                strcat(chrarry_CommandLinePath, de_DirEntity->d_name);
                strcat(chrarry_CommandLinePath, "/cmdline");
                FILE* fd_CmdLineFile = fopen(chrarry_CommandLinePath, "rt"); //open the file for reading text
                if (fd_CmdLineFile) {
                    fscanf(fd_CmdLineFile, "%s", chrarry_NameOfProcess); //read from /proc/<NR>/cmdline
                    fclose(fd_CmdLineFile); //close the file prior to exiting the routine

                    chrptr_StringToCompare = chrarry_NameOfProcess;
                    if (CompareFunction(chrptr_StringToCompare,
                            cchrptr_ProcessName, intCaseSensitiveness)) {
                        pid_ProcessIdentifier = (pid_t) atoi(
                                de_DirEntity->d_name);
                        closedir(dir_proc);
                        return pid_ProcessIdentifier;
                    }
                }
            }
        }
    }
    closedir(dir_proc);
    return pid_ProcessIdentifier;
}


/**
 * 檢測服務,如果不存在服務則啓動
 */
void check_and_restart_activity(char* service) {

    char cmdline[200];
    sprintf(cmdline, "am start -n %s", service);
    char tmp[200];
    sprintf(tmp, "cmd=%s", cmdline);
    ExecuteCommandWithPopen(cmdline, tmp, 200);
}

JNIEXPORT jstring JNICALL Java_lv_anto_com_ndktest_NativeRuntime_stringFromJNI
  (JNIEnv * env, jobject thiz){
  #if defined(__arm__)
        #if defined(__ARM_ARCH_7A__)
            #if defined(__ARM_NEON__)
                #define ABI "armeabi-v7a/NEON"
            #else
                #define ABI "armeabi-v7a"
            #endif
        #else
            #define ABI "armeabi"
        #endif
    #elif defined(__i386__)
        #define ABI "x86"
    #elif defined(__mips__)
        #define ABI "mips"
    #else
        #define ABI "unknown"
    #endif
    return (*env)->NewStringUTF(env,
            "Hello from JNI !  Compiled with ABI " ABI ".");
  }


/**
 * jstring 轉 String
 */
char* jstringTostring(JNIEnv* env, jstring jstr) {
    char* rtn = NULL;
    jclass clsstring = (*env)->FindClass(env, "java/lang/String");
    jstring strencode = (*env)->NewStringUTF(env, "utf-8");
    jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes",
            "(Ljava/lang/String;)[B");
    jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid,
            strencode);
    jsize alen = (*env)->GetArrayLength(env, barr);
    jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
    if (alen > 0) {
        rtn = (char*) malloc(alen + 1);
        memcpy(rtn, ba, alen);
        rtn[alen] = 0;
    }
    (*env)->ReleaseByteArrayElements(env, barr, ba, 0);
    return rtn;
}



/**
 * 查找進程
 */
pid_t JNICALL Java_com_yyh_fork_NativeRuntime_findProcess(JNIEnv* env,
        jobject thiz, jstring cchrptr_ProcessName) {
    char * rtn = jstringTostring(env, cchrptr_ProcessName);
    //return 1;
    return GetPIDbyName_implements(rtn, 0, 0); //大小寫不敏感 sub串匹配
}

/**
 * 啓動Service
 */

JNIEXPORT void JNICALL Java_lv_anto_com_ndktest_NativeRuntime_startService
  (JNIEnv * env, jobject thiz, jstring cchrptr_ProcessName, jstring sdpath){
    char * rtn = jstringTostring(env, cchrptr_ProcessName); // 得到進程名稱
    char * sd = jstringTostring(env, sdpath);
    a = rtn;
    start(1, rtn, sd);

  }
 /**
 *關閉Service
 **/
JNIEXPORT jint JNICALL Java_lv_anto_com_ndktest_NativeRuntime_stopService
  (JNIEnv * env, jobject thiz){
    exit(0);
  }


/*
 * Class:     lv_anto_com_ndktest_NativeRuntime
 * Method:    findProcess
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_lv_anto_com_ndktest_NativeRuntime_findProcess
  (JNIEnv * env, jobject thiz, jstring cchrptr_ProcessName){
    char * rtn = jstringTostring(env, cchrptr_ProcessName);
    //return 1;
    return GetPIDbyName_implements(rtn, 0, 0); //大小寫不敏感 sub串匹配
  }

  JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env = NULL;
    jint result = -1;

    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return result;
    }
    return JNI_VERSION_1_4;
  }

六.上面的事情都幹完了,我們該進行ndk-build進行編譯生成.os文件啦。
七.上面的事情弄完了以後呢,我們要想起來我們還有一個gradle文件要配置啊

apply plugin: 'com.android.application'

android {
    compileSdkVersion 24
    buildToolsVersion "24.0.2"
    defaultConfig {
        applicationId "lv.anto.com.ndktest"
        minSdkVersion 17
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        ndk {
            moduleName "NativeRuntime"         //生成的so名字
            ldLibs "log"//實現__android_log_print
            abiFilters "armeabi", "armeabi-v7a", "x86"  //輸出指定三種abi體系結構下的so庫。目前可有可無。
        }
        sourceSets {
            main {
                jniLibs.srcDirs = ["libs"]

            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }


}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:24.2.1'
    testCompile 'junit:junit:4.12'
}

八.又弄完了,就是這麼簡單,哈哈,接下來就是調用了;
1.在main的主activity中調用

package lv.anto.com.ndktest;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {

    Button btnstart, btnend;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initService();

    }
    private void initService() {
        btnstart = (Button) findViewById(R.id.btn_start);
        btnend = (Button) findViewById(R.id.btn_end);

        Toast.makeText(this, NativeRuntime.getInstance().stringFromJNI(), Toast.LENGTH_LONG).show();
        String executable = "libNativeRuntime.so";
        String aliasfile = "NativeRuntime";
        String parafind = "/data/data/" + getPackageName() + "/" + aliasfile;
        String retx = "false";
        NativeRuntime.getInstance().RunExecutable(getPackageName(), executable, aliasfile, getPackageName() + "/lv.anto.com.ndktest.HostMonitor");
        NativeRuntime.getInstance().startService(getPackageName() + "/lv.anto.com.ndktest.HostMonitor", FileUtils.createRootPath());


        btnstart.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                (new Thread(new Runnable() {
                    public void run() {
                        try {
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                })).start();
            }
        });

        btnend.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                try {
                    NativeRuntime.getInstance().stopService();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

    }

}

2.在BroadcastReceiver中進行配置開機啓動

package lv.anto.com.ndktest;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class PhoneStatReceiver extends BroadcastReceiver {

    private String TAG = "tag";  

    @Override  
    public void onReceive(Context context, Intent intent) {
        if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {  
            Log.i(TAG, "手機開機了~~");
            NativeRuntime.getInstance().startService(context.getPackageName() +
                    "/lv.anto.com.ndktest.HostMonitor"
                    , FileUtils.createRootPath());
        } else if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {  
        }  
    }  


} 

下面我再把工具類的代碼粘出來;

package lv.anto.com.ndktest;

import java.io.File;
import java.io.FileOutputStream;

import android.os.Environment;

/**
 * 文件工具類
 * 
 * @author king
 * 
 */
public class FileUtils {
    // 根緩存目錄
    private static String cacheRootPath = "";

    /**
     * sd卡是否可用
     * 
     * @return
     */
    public static boolean isSdCardAvailable() {
        return Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED);
    }

    /**
     * 創建根緩存目錄
     * 
     * @return
     */
    public static String createRootPath() {
        if (isSdCardAvailable()) {
            // /sdcard/Android/data/<application package>/cache
            cacheRootPath = App.mContext.getExternalCacheDir()
                    .getPath();
        } else {
            // /data/data/<application package>/cache
            cacheRootPath = App.mContext.getCacheDir().getPath();
        }
        return cacheRootPath;
    }

    /**
     * 創建文件夾
     * 
     * @param dirPath
     * @return 創建失敗返回""
     */
    private static String createDir(String dirPath) {
        try {
            File dir = new File(dirPath);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            return dir.getAbsolutePath();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dirPath;
    }

    /**
     * 獲取圖片緩存目錄
     * 
     * @return 創建失敗,返回""
     */
    public static String getImageCachePath() {
        String path = createDir(createRootPath() + File.separator + "img"
                + File.separator);
        return path;
    }

    /**
     * 獲取圖片裁剪緩存目錄
     * 
     * @return 創建失敗,返回""
     */
    public static String getImageCropCachePath() {
        String path = createDir(createRootPath() + File.separator + "imgCrop"
                + File.separator);

        return path;
    }

    /**
     * 刪除文件或者文件夾
     * 
     * @param file
     */
    public static void deleteFileOrDirectory(File file) {
        try {
            if (file.isFile()) {
                file.delete();
                return;
            }
            if (file.isDirectory()) {
                File[] childFiles = file.listFiles();
                // 刪除空文件夾
                if (childFiles == null || childFiles.length == 0) {
                    file.delete();
                    return;
                }
                // 遞歸刪除文件夾下的子文件
                for (int i = 0; i < childFiles.length; i++) {
                    deleteFileOrDirectory(childFiles[i]);
                }
                file.delete();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 將內容寫入文件
     * 
     * @param filePath
     *            eg:/mnt/sdcard/demo.txt
     * @param content
     *            內容
     */
    public static void writeFileSdcard(String filePath, String content,
            boolean isAppend) {

        try {
            FileOutputStream fout = new FileOutputStream(filePath, isAppend);
            byte[] bytes = content.getBytes();

            fout.write(bytes);

            fout.close();

        } catch (Exception e) {

            e.printStackTrace();

        }
    }
}

剩下的事情大家就可以自行解決了,佈局文件中只有兩個按鈕,別忘了配置AndroidManifest.xml文件;

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"

    package="lv.anto.com.ndktest">

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        android:name=".App"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver
            android:name=".PhoneStatReceiver"
            android:enabled="true"
            android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.USER_PRESENT" />
            </intent-filter>
        </receiver>

        <service
            android:name=".HostMonitor"
            android:enabled="true"
            android:exported="true"
            android:process=":remote" />
    </application>

</manifest>

總結:這些就弄完了,一個賤賤的不死的服務就這樣搞定了,是不是感覺很簡單呢?說實話,對於C總的東西還是有些不瞭解的,希望以後能夠進步瞭解,也同樣希望看到這個文章的你,能夠寫出來這麼賤的服務,對於社交類,後臺服務類的app還是很有幫助的,猶如我之前寫過的一片文章,我覺得這就是一種冥冥之中的趨勢;哈哈,一起加油進步吧;很晚了,晚安各位;

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