普通Socket通信
客户端代码:
unix_client.c
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#define MAXLINE 80
char *client_path = "client-socket";
char *server_path = "server-socket";
int main() {
struct sockaddr_un cliun, serun;
int len;
char buf[100];
int sockfd, n;
if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){
perror("client socket error");
exit(1);
}
// signal(SIGPIPE, SIG_IGN);
memset(&serun, 0, sizeof(serun));
serun.sun_family = AF_UNIX;
strncpy(serun.sun_path,server_path ,
sizeof(serun.sun_path) - 1);
if (connect(sockfd, (struct sockaddr *)&serun, sizeof(struct sockaddr_un)) < 0){
perror("connect error");
exit(1);
}
printf("please input send char:");
while(fgets(buf, MAXLINE, stdin) != NULL) {
write(sockfd, buf, strlen(buf));
n = read(sockfd, buf, MAXLINE);
if ( n <= 0 ) {
printf("the other side has been closed.\n");
break;
}else {
printf("received from server: %s \n",buf);
}
printf("please input send char:");
}
printf("end server date");
close(sockfd);
return 0;
}
服务端代码:
unix_server.c
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#define MAXLINE 80
char *socket_path = "server-socket";
int main()
{
struct sockaddr_un serun, cliun;
socklen_t cliun_len;
int listenfd, connfd, size;
char buf[MAXLINE];
int i, n;
if ((listenfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
perror("socket error");
exit(1);
}
memset(&serun, 0, sizeof(serun));
serun.sun_family = AF_UNIX;
strncpy(serun.sun_path,socket_path ,
sizeof(serun.sun_path) - 1);
unlink(socket_path);
if (bind(listenfd, (struct sockaddr *)&serun, sizeof(struct sockaddr_un)) < 0) {
perror("bind error");
exit(1);
}
printf("UNIX domain socket bound\n");
if (listen(listenfd, 20) < 0) {
perror("listen error");
exit(1);
}
printf("Accepting connections ...\n");
while(1) {
cliun_len = sizeof(cliun);
if ((connfd = accept(listenfd, (struct sockaddr *)&cliun, &cliun_len)) < 0){
perror("accept error");
continue;
}
printf("new client connect to server,client sockaddr === %s \n",((struct sockaddr *)&cliun)->sa_data);
while(1) {
memset(buf,0,sizeof(buf));
n = read(connfd, buf, sizeof(buf));
if (n < 0) {
perror("read error");
break;
} else if(n == 0) {
printf("EOF\n");
break;
}
printf("received: %s %d %d\n ", buf, sizeof(buf),sizeof("quit"));
if (strncmp(buf,"quit",4) == 0) {
break;
}
for(i = 0; i < n; i++) {
buf[i] = toupper(buf[i]);
}
write(connfd, buf, n);
}
close(connfd);
}
close(listenfd);
return 0;
}
epoll代码:
真实的场景很可能有很多个客户端连接服务端,那这个时候服务端 是怎么做到和客户端同时通信的呢? I/O 多路复用技术(I/O multiplexing)。复用是指在同一个进程(线程)中,处理多路 I/O,多路指多个文件描述符。它核心思想收集进程感兴趣的全部描述符,然后调用一个函数,当这些描述符中的一个或多个准备好 I/O 时,函数返回并告知进程是哪些描述符准备好了。此时进程只需要去这些准备好的描述符上操作即可。
服务端代码:
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/epoll.h>
#define MAXLINE 80
char *socket_path = "server-socket";
int main()
{
struct sockaddr_un serun, cliun;
socklen_t cliun_len;
int listenfd, connfd, size;
char buf[MAXLINE];
int i, n;
if ((listenfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
perror("socket error");
exit(1);
}
memset(&serun, 0, sizeof(serun));
serun.sun_family = AF_UNIX;
strncpy(serun.sun_path,socket_path ,
sizeof(serun.sun_path) - 1);
unlink(socket_path);
if (bind(listenfd, (struct sockaddr *)&serun, sizeof(struct sockaddr_un)) < 0) {
perror("bind error");
exit(1);
}
printf("UNIX domain socket bound\n");
if (listen(listenfd, 20) < 0) {
perror("listen error");
exit(1);
}
printf("Accepting connections ...\n");
// 4. 创建epoll树
int epfd = epoll_create(1000);
if(epfd == -1)
{
perror("epoll_create");
exit(-1);
}
//5、将用于监听的lfd挂的epoll树上(红黑树)
struct epoll_event ev;//这个结构体记录了检测什么文件描述符的什么事件
ev.events = EPOLLIN;
ev.data.fd = listenfd;
epoll_ctl(epfd, EPOLL_CTL_ADD, listenfd, &ev);//ev里面记录了检测lfd的什么事件
// 循环检测 委托内核去处理
struct epoll_event events[1024];//当内核检测到事件到来时会将事件写到这个结构体数组里
while(1) {
int num = epoll_wait(epfd, events, sizeof(events)/sizeof(events[0]), -1);//最后一个参数 表示阻塞
for (int i = 0;i<num;i++) {
if(events[i].data.fd == listenfd)//有连接请求到来
{
int len = sizeof(cliun);
int connfd = accept(listenfd, (struct sockaddr *)&cliun, &len);
if(connfd == -1)
{
perror("accept");
exit(-1);
}
printf("a new client connected! \n");
//将用于通信的文件描述符挂到epoll树上
ev.data.fd = connfd;
ev.events = EPOLLIN;
epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &ev);
} else {
//通信也有可能是写事件
if(events[i].events & EPOLLOUT)
{
//这里先忽略写事件
continue;
}
char buf[1024]={0};
int count = read(events[i].data.fd, buf, sizeof(buf));
if(count == 0)//客户端关闭了连接
{
printf("客户端关闭了连接。。。。\n");
//将对应的文件描述符从epoll树上取下
close(events->data.fd);
epoll_ctl(epfd, EPOLL_CTL_DEL, events->data.fd, NULL);
}
else
{
if(count == -1)
{
perror("read");
exit(-1);
}
else
{
//正常通信
printf("client say: %s\n" ,buf);
write(events[i].data.fd, buf, strlen(buf)+1);
}
}
}
}
}
close(listenfd);
return 0;
}
客户端代码:
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#define MAXLINE 80
char *client_path = "client-socket";
char *server_path = "server-socket";
int main() {
struct sockaddr_un cliun, serun;
int len;
char buf[100];
int sockfd, n;
if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){
perror("client socket error");
exit(1);
}
// signal(SIGPIPE, SIG_IGN);
memset(&serun, 0, sizeof(serun));
serun.sun_family = AF_UNIX;
strncpy(serun.sun_path,server_path ,
sizeof(serun.sun_path) - 1);
if (connect(sockfd, (struct sockaddr *)&serun, sizeof(struct sockaddr_un)) < 0){
perror("connect error");
exit(1);
}
printf("please input send char:");
while(fgets(buf, MAXLINE, stdin) != NULL) {
write(sockfd, buf, strlen(buf));
n = read(sockfd, buf, MAXLINE);
if ( n <= 0 ) {
printf("the other side has been closed.\n");
break;
}else {
printf("received from server: %s \n",buf);
}
printf("please input send char:");
}
printf("end server date");
close(sockfd);
return 0;
}
Socket让app实现shell命令行执行
找到一个有执行shell权限的程序,然后和这个程序进行一个跨进程通信,app把要执行的命令发送给这个高权限的程序,程序就会执行app发送过来的命令,这个时候再跨进程返回结果给app既可以;
MyApplicationB就是我们普通第三方程序
rootServer就是我们写的native执行,负责和MyApplicationB进行通信和执行shell命令
adbd就是负责来拉起rootServer
具体实现步骤:
主要把main.cpp覆盖到项目的:system/core/adb/daemon/main.cpp 然后在重新编译系统:make
其实这里核心就修改了一句代码而已:
StartSubprocess("./system/bin/rootServer", nullptr, SubprocessType::kRaw,
SubprocessProtocol::kNone);
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define TRACE_TAG ADB
#include "sysdeps.h"
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <sys/prctl.h>
#include <memory>
#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <libminijail.h>
#include <log/log_properties.h>
#include <scoped_minijail.h>
#include <private/android_filesystem_config.h>
#include "debuggerd/handler.h"
#include "selinux/android.h"
#include "adb.h"
#include "adb_auth.h"
#include "adb_listeners.h"
#include "adb_utils.h"
#include "transport.h"
#include "shell_service.h"
#include "mdns.h"
static const char* root_seclabel = nullptr;
static void drop_capabilities_bounding_set_if_needed(struct minijail *j) {
#if defined(ALLOW_ADBD_ROOT)
if (__android_log_is_debuggable()) {
return;
}
#endif
minijail_capbset_drop(j, CAP_TO_MASK(CAP_SETUID) | CAP_TO_MASK(CAP_SETGID));
}
static bool should_drop_privileges() {
#if defined(ALLOW_ADBD_ROOT)
// The properties that affect `adb root` and `adb unroot` are ro.secure and
// ro.debuggable. In this context the names don't make the expected behavior
// particularly obvious.
//
// ro.debuggable:
// Allowed to become root, but not necessarily the default. Set to 1 on
// eng and userdebug builds.
//
// ro.secure:
// Drop privileges by default. Set to 1 on userdebug and user builds.
bool ro_secure = android::base::GetBoolProperty("ro.secure", true);
bool ro_debuggable = __android_log_is_debuggable();
// Drop privileges if ro.secure is set...
bool drop = ro_secure;
// ... except "adb root" lets you keep privileges in a debuggable build.
std::string prop = android::base::GetProperty("service.adb.root", "");
bool adb_root = (prop == "1");
bool adb_unroot = (prop == "0");
if (ro_debuggable && adb_root) {
drop = false;
}
// ... and "adb unroot" lets you explicitly drop privileges.
if (adb_unroot) {
drop = true;
}
return drop;
#else
return true; // "adb root" not allowed, always drop privileges.
#endif // ALLOW_ADBD_ROOT
}
static void drop_privileges(int server_port) {
ScopedMinijail jail(minijail_new());
// Add extra groups:
// AID_ADB to access the USB driver
// AID_LOG to read system logs (adb logcat)
// AID_INPUT to diagnose input issues (getevent)
// AID_INET to diagnose network issues (ping)
// AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
// AID_SDCARD_R to allow reading from the SD card
// AID_SDCARD_RW to allow writing to the SD card
// AID_NET_BW_STATS to read out qtaguid statistics
// AID_READPROC for reading /proc entries across UID boundaries
// AID_UHID for using 'hid' command to read/write to /dev/uhid
gid_t groups[] = {AID_ADB, AID_LOG, AID_INPUT, AID_INET,
AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW,
AID_NET_BW_STATS, AID_READPROC, AID_UHID};
minijail_set_supplementary_gids(jail.get(), arraysize(groups), groups);
// Don't listen on a port (default 5037) if running in secure mode.
// Don't run as root if running in secure mode.
if (should_drop_privileges()) {
drop_capabilities_bounding_set_if_needed(jail.get());
minijail_change_gid(jail.get(), AID_SHELL);
minijail_change_uid(jail.get(), AID_SHELL);
// minijail_enter() will abort if any priv-dropping step fails.
minijail_enter(jail.get());
D("Local port disabled");
} else {
// minijail_enter() will abort if any priv-dropping step fails.
minijail_enter(jail.get());
if (root_seclabel != nullptr) {
if (selinux_android_setcon(root_seclabel) < 0) {
LOG(FATAL) << "Could not set SELinux context";
}
}
std::string error;
std::string local_name =
android::base::StringPrintf("tcp:%d", server_port);
if (install_listener(local_name, "*smartsocket*", nullptr, 0, nullptr, &error)) {
LOG(FATAL) << "Could not install *smartsocket* listener: " << error;
}
}
}
static void setup_port(int port) {
local_init(port);
setup_mdns(port);
}
int adbd_main(int server_port) {
umask(0);
signal(SIGPIPE, SIG_IGN);
init_transport_registration();
// We need to call this even if auth isn't enabled because the file
// descriptor will always be open.
adbd_cloexec_auth_socket();
if (ALLOW_ADBD_NO_AUTH && !android::base::GetBoolProperty("ro.adb.secure", false)) {
auth_required = false;
}
adbd_auth_init();
// Our external storage path may be different than apps, since
// we aren't able to bind mount after dropping root.
const char* adb_external_storage = getenv("ADB_EXTERNAL_STORAGE");
if (adb_external_storage != nullptr) {
setenv("EXTERNAL_STORAGE", adb_external_storage, 1);
} else {
D("Warning: ADB_EXTERNAL_STORAGE is not set. Leaving EXTERNAL_STORAGE"
" unchanged.\n");
}
drop_privileges(server_port);
StartSubprocess("./system/bin/rootServer", nullptr, SubprocessType::kRaw,
SubprocessProtocol::kNone);
bool is_usb = false;
if (access(USB_FFS_ADB_EP0, F_OK) == 0) {
// Listen on USB.
usb_init();
is_usb = true;
}
// If one of these properties is set, also listen on that port.
// If one of the properties isn't set and we couldn't listen on usb, listen
// on the default port.
std::string prop_port = android::base::GetProperty("service.adb.tcp.port", "");
if (prop_port.empty()) {
prop_port = android::base::GetProperty("persist.adb.tcp.port", "");
}
int port;
if (sscanf(prop_port.c_str(), "%d", &port) == 1 && port > 0) {
D("using port=%d", port);
// Listen on TCP port specified by service.adb.tcp.port property.
setup_port(port);
} else if (!is_usb) {
// Listen on default port.
setup_port(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
}
D("adbd_main(): pre init_jdwp()");
init_jdwp();
D("adbd_main(): post init_jdwp()");
D("Event loop starting");
fdevent_loop();
return 0;
}
int main(int argc, char** argv) {
while (true) {
static struct option opts[] = {
{"root_seclabel", required_argument, nullptr, 's'},
{"device_banner", required_argument, nullptr, 'b'},
{"version", no_argument, nullptr, 'v'},
};
int option_index = 0;
int c = getopt_long(argc, argv, "", opts, &option_index);
if (c == -1) {
break;
}
switch (c) {
case 's':
root_seclabel = optarg;
break;
case 'b':
adb_device_banner = optarg;
break;
case 'v':
printf("Android Debug Bridge Daemon version %d.%d.%d\n", ADB_VERSION_MAJOR,
ADB_VERSION_MINOR, ADB_SERVER_VERSION);
return 0;
default:
// getopt already prints "adbd: invalid option -- %c" for us.
return 1;
}
}
close_stdin();
debuggerd_init(nullptr);
adb_trace_init(argv);
D("Handling main()");
return adbd_main(DEFAULT_ADB_PORT);
}
rootServer代码:
SocketServer.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <utils/Log.h>
#include <sys/epoll.h>
#define MAXFILE 65535 // 最大的文件描述符
char* result;
long lenght = 8192;
char *socket_path = "server-socket";
void executeCMD(const char *cmd) {
ALOGE(" executeCMD \n");
char buf_ps[8192];
char ps[8192] = { 0 };
int i = 1;
char *result2 = NULL;
FILE *ptr = NULL;
strcpy(ps, cmd);
if ((ptr = popen(ps, "r")) != NULL) {
result = (char *) malloc(lenght * sizeof(char));
char *result2 = (char *) malloc(lenght * sizeof(char));
while (fgets(buf_ps, 8192, ptr) != NULL) {
result = (char *) malloc(lenght * i * sizeof(char));
if (result2 != NULL)
strcpy(result, result2);
strcat(result, buf_ps);
i++;
result2 = (char *) malloc(lenght * (i - 1) * sizeof(char));
strcpy(result2, result);
ALOGE(" executeCMD result = %s\n",result);
}
pclose(ptr);
ptr = NULL;
} else {
printf("popen %s error\n", ps);
}
}
int main() {
printf("main rootServer running \n");
struct sockaddr_un serun, cliun;
socklen_t cliun_len;
int listenfd, connfd, size;
char buf[8192];
int i, n;
if ((listenfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
perror("socket error");
exit(1);
}
memset(&serun, 0, sizeof(serun));
serun.sun_family = AF_UNIX;
serun.sun_path[0] = 0;
strcpy(serun.sun_path+1,socket_path);
socklen_t addrlen_ = sizeof(serun.sun_family) + strlen(socket_path) + 1;
//unlink(socket_path);
if (bind(listenfd, (struct sockaddr *)&serun, addrlen_) < 0) {
perror("bind error");
exit(1);
}
if (listen(listenfd, 20) < 0) {
perror("listen error");
exit(1);
}
while (1) // 守护进程实现的服务
{
ALOGE("wait connect...\n");
printf("wait connect...\n");
socklen_t l = sizeof(struct sockaddr_un);
if ((connfd = accept(listenfd,(struct sockaddr *) &cliun, &l)) < 0) {
perror("accept");
//return 1;
}
int len =0;
if ((len = recv(connfd, buf, 8192, 0)) > 0) {
ALOGE("connect recv\n");
buf[len] = '\0';
printf("%s\n", buf);
executeCMD(buf);
if (strlen(result) == 0) {
strcpy(result, "Returing is null!");
}
if (send(connfd, result, strlen(result), 0) < 0) {
perror("write");
//return 1;
}
}
close(connfd);
}
close(listenfd);
return 0;
}
将该可执行程序编译到系统里面
Android.mk
include $(CLEAR_VARS)
LOCAL_SRC_FILES := SocketServer.c
LOCAL_MODULE := rootServer
LOCAL_SHARED_LIBRARIES :=liblog
LOCAL_PRELINK_MODULE := false
include $(BUILD_EXECUTABLE)
编译步骤:1 :make rootServer //成功会生成rootServer到system/bin/ 下面
2 :接下来就是要让rootServer打包到img,
这里可以:touch frameworks/base/cmds/bootanimation/BootAnimation.cpp
目的是为了让系统可以检测到bootanimation有更新需要重新打包
3:直接进行 make既可以,因为只是为了触发打包,所以非常快
4:emulator启动模拟器
app端关键代码:
TcpMainActivity.java
package com.panzq.applicationb;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.os.PersistableBundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.SocketAddress;
public class TcpMainActivity extends AppCompatActivity implements SafeHandler.Callback {
LocalSocket lsocket = null;
BufferedWriter br;
Handler handler;
EditText sendCmdText = null;
TextView receiveText = null;
private static final int HANDLER_SENDMESSAGE = 0x100;
@Override
protected void onStop() {
super.onStop();
}
@Override
protected void onPause() {
super.onPause();
}
@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tcp_activity_main);
Button starActivityA = findViewById(R.id.send);
sendCmdText = findViewById(R.id.send_cmd);
receiveText = findViewById(R.id.receiveText);
HandlerThread handlerThread = new HandlerThread("mythread");
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
starActivityA.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.i("test","rootclient send cmd to rootServer cmd = " + sendCmdText.getText().toString());
new Thread(new Runnable() {
@Override
public void run() {
sendMessage();
}
}).start();
// Bundle bundle = getContentResolver().call(Uri.parse("content://com.android.shell.execute"),"cmd",sendCmdText.getText().toString(),null);
// if (bundle!= null) {
// String result = bundle.getString("result");
// receiveText.setText(result);
// }
}
});
Button provier = findViewById(R.id.provider);
provier.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
Log.i("test", "Runtime.getRuntime().exec call arg = " + sendCmdText.getText().toString());
Process p = Runtime.getRuntime().exec(sendCmdText.getText().toString());
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
String str = null;
while ((line = in.readLine()) != null) {
str += line + "\n";
Log.i("test", "Runtime.getRuntime().exec resultline = " + line);
}
receiveText.setText(str);
} catch (Exception e) {
e.printStackTrace();
Log.i("test", "Runtime.getRuntime().exec Exception " ,e);
}
}
});
}
private void sendMessage() {
try {
lsocket = new LocalSocket();
LocalSocketAddress address = new LocalSocketAddress("server-socket", LocalSocketAddress.Namespace.ABSTRACT);
lsocket.connect(address);
String result;
Log.i("test","rootclient send cmd to rootServer cmd = " + sendCmdText.getText().toString());
br = new BufferedWriter(new OutputStreamWriter(lsocket.getOutputStream()));
br.write(sendCmdText.getText().toString());
br.newLine();
br.flush();
// br.close();
Log.d("test", "========发送成功========");
InputStream inputStream = lsocket.getInputStream();
byte[] buf = new byte[inputStream.available() -1];
inputStream.read(buf,0,inputStream.available() -1) ;
String str =new String(buf);
Log.d("test", "========接受成功======== inputStream.available() = " +inputStream.available() + "read str = " +str);
final String str1 = str;
runOnUiThread(new Runnable() {
@Override
public void run() {
receiveText.setText(str1);
}
});
lsocket.close();
} catch (IOException e) {
e.printStackTrace();
Log.i("test","rootclient rootServer 发送失败",e);
}
}
@Override
public void handlerMessage(Message msg) {
switch (msg.what) {
case HANDLER_SENDMESSAGE:
sendMessage();
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(null);
}
}
SafeHandler.java
package com.panzq.applicationb;
import android.os.Handler;
import android.os.Message;
import java.lang.ref.WeakReference;
/**
* 解决内存泄漏的Handler
*
* @author hq
*/
public class SafeHandler extends Handler {
public interface Callback {
/**
* 消息处理回调
*
* @param msg 消息
*/
void handlerMessage(Message msg);
}
private WeakReference<Callback> cbRef;
public SafeHandler(SafeHandler.Callback cb) {
cbRef = new WeakReference<>(cb);
}
@Override
public void handleMessage(Message msg) {
Callback cb = cbRef.get();
if (cb != null) {
cb.handlerMessage(msg);
}
}
/**
* 发送消息。在发送前会先清空,避免消息栈中包括多个相同的
*
* @param what 需要发送的消息
* @param delayMillis 延迟时间
*/
public void sendMessageOnly(int what, int delayMillis) {
removeMessages(what);
super.sendEmptyMessageDelayed(what, delayMillis);
}
/**
* 从消息队列中移除多个消息
*
* @param messages 需要移除的消息列表
*/
public void removeMessages(int... messages) {
if (messages == null) {
return;
}
for (int msg : messages) {
removeMessages(msg);
}
}
public void clear() {
removeCallbacksAndMessages(null);
}
}