开年第一篇贱贱的Android NDK服务

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lvzhongdi/article/details/53984113

首先先祝大家新年快乐,晚了一点哈,写这篇文章主要是因为项目中需要,我暂时性的做一个记录,首先要感谢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还是很有帮助的,犹如我之前写过的一片文章,我觉得这就是一种冥冥之中的趋势;哈哈,一起加油进步吧;很晚了,晚安各位;

猜你喜欢

转载自blog.csdn.net/lvzhongdi/article/details/53984113
今日推荐