⑴ 如何對自身應用進行卸載監聽
前段時間有個同事問我android應用在卸載以後,如何能夠通知一下伺服器,讓用戶填寫一下卸載的原因,以求為將來的應用修改積累數據。當時他是有段源代碼的,但是有點小問題,我只是幫他定位一下了代碼的問題,具體細節沒有研究。又加上最近工作比較繁忙,所以就放下來了,今天稍微有點空,就自己做了一個應用demo,告知一下諸位在android中如何做到監聽自身應用被卸載了。
一 效果演示
打開應用效果圖:
圖1
點擊卸載後提示,如下圖:
圖2
然後退出應用,卸載程序,會發現當應用被卸載以後,會彈出調用瀏覽器的提示,這里隨便放了一個搜狐瀏覽頁面,在自己的應用中應該調用的一般都是調查頁面。如下圖:
圖3
ok,效果前面已經演示了,現在需要討論一下其具體實現了。
首先,通過adb shell進入手機,然後第一次進入應用,,像圖1一樣,不點擊按鈕,通過 ps | busybox grep ubuntu 看這個應用的進程信息,如下圖:
這個時候只有
u0_a108 2953 124 490956 47792 ffffffff 40052a40 S com.example.ubuntuforandroid
2953 這一個進程
點擊 卸載後提示 按鈕再次,執行剛才執行的ps命令,發現已經有兩個進程了如下圖:
其實新產生的進程是通過程序調用jni介面
public static native int Reguninstall(String path,String url);
這個介面fork了一個進程,而新fork的進程負責監聽本應用是否被卸載了
二 源碼分析
java層的代碼如下,很簡單,就是調用一下jni介面
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initInjectFunction("testfile"); test = (TextView)this.findViewById(R.id.testview); test.setText("點擊卸載後提示按鈕,你的應用在卸載以後會調用瀏覽器,然後調用你需要的頁面。"); btn = (Button)this.findViewById(R.id.testbtn); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub String directory = MainActivity.this.getFilesDir().getAbsolutePath(); String url = "http://www.sohu.com/"; JniExec.Reguninstall(directory,url); test.setText("現在可以退出應用,然後卸載應用,看看是否有效果"); } }); }
一目瞭然,不用多言了
現在就分析
package com.example.ubuntuforandroid; public class JniExec { static { System.loadLibrary("uninstall"); } public static native int Reguninstall(String path,String url); }
Reguninstall 這個jni介面裡面做了什麼事情,能夠達到監聽本身應用卸載的效果。
native代碼分析
jint Java_com_example_ubuntuforandroid_JniExec_Reguninstall(JNIEnv* env, jobject thiz, jstring path, jstring url) { LOGI("Java_com_example_ubuntuforandroid_JniExec_Reguninstall"); char *listenpath = (char*) (*env)->GetStringUTFChars(env,path, 0); char *jumpurl = (char*) (*env)->GetStringUTFChars(env,url, 0); LOGI("notify path is %s",listenpath); LOGI("jumpurl is %s",jumpurl); pid_t pid; pid = fork(); if(pid == 0) { //子進程 inotify_main(listenpath,jumpurl); } //父進程不阻塞調用 waitpid ok 子進程變成了孤兒進程,被init進程收養了 pid = waitpid(-1,0,1); LOGI("father bye bye"); return 0; }
這個介面裡面最關鍵的是調用了 inotify_main 這個函數。如果看這段代碼比較費力的話,建議先弄清楚linux 下的fork機制,搞清楚 孤兒進程 僵屍進程這些如何產生的情況。
下面看 inotify_main 這個函數
void inotify_main(char *path,char *url) { struct pollfd poll_list[2]; poll_list[0].fd = inotify_init(); poll_list[0].events = POLLIN; int wd = inotify_add_watch(poll_list[0].fd, path, IN_DELETE | IN_CREATE); if(wd < 0) { fprintf(stderr, "could not add watch for %s, %s\n", path, strerror(errno)); return ; } int retval; while(1) { retval = poll(poll_list,(unsigned long)1,-1); /* retval 總是大於0或為-1,因為我們在阻塞中工作 */ LOGI("retval = %d\n",retval); if(retval < 0) { fprintf(stderr,"poll錯誤: %s/n",strerror(errno)); return; } if((poll_list[0].revents & POLLIN) == POLLIN) { LOGI("poll_list[0].revents&POLLIN\n"); inotify_handle(poll_list[0].fd,url); } } inotify_rm_watch(poll_list[0].fd,wd); }
⑵ C語言對網路的操作和C程序的界面問題!
------------------
你學習C語言的網路編程,我建議你到LINUX下去學
給你一個例子程序是一個區域網聊天伺服器
------------------
#define SAY_HELLO 0 /*發送歡迎信息*/
#define SEND_MESSAGE 1 /*轉發聊天信息*/
#define CHANGE_NAME 2 /*更改用戶名*/
#define REFLESH_LIST 3 /*刷新用戶名列表*/
#define CLIENT_LEFT 4 /*客戶端離開*/
#define CLIENT_COME 5 /*客戶加入*/
#define SEND_RECV_FILE 6 /*發送接收文件*/
#define END_OF_FILE 7 /*文件發送結束標記*/
#define FILE_BLOCK 1024 /*讀取文件的塊大小*/
#define MAX_CHATER 100 /*伺服器最大同時容納人數*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netdb.h>
#include <fcntl.h>
#include <bits/time.h>
/*服務端發送歡迎信息*/
struct Message0{
int Func_number; /*功能號: 此處取0 SAY_HELLO
1------表示轉發信息
2------表示轉發更名通知
3------表示傳送當前用戶列表
4------表示客戶端離開
5------表示客戶加入
6------表示發送和接收文件
*/
char msg[50]; /* 歡迎信息 */
char username[50]; /* 伺服器端生成的用戶名稱 */
};
/*伺服器端與客戶端信息轉發數據結構*/
struct Message1{
int Func_number; /*功能號: 此處取1 SEND_MESSAGE
1------表示轉發信息
2------表示轉發更名通知
3------表示傳送當前用戶列表
4------表示客戶端離開
5------表示客戶加入
6------表示發送和接收文件
*/
char msg[201]; /* 轉發的信息 */
char send_user_name [50]; /* 發送數據的用戶呢稱 */
char recv_user_name [50]; /* 接收數據的用戶名稱 */
};
/*伺服器端轉發更名通知*/
struct Message2{
int Func_number; /*功能號: 此處取2 CHANGE_NAME
1------表示轉發信息
2------表示轉發更名通知
3------表示傳送當前用戶列表
4------表示客戶端離開
5------表示客戶加入
6------表示發送和接收文件
*/
char oldname[50]; /* 轉發更名通知信息 */
char newname[50];
};
/*伺服器保存當前所有用戶列表的數據結構*/
struct User{
char user_name[50]; /* 用戶呢稱 */
char user_address[15]; /* 該用戶IP地址 */
};
/*伺服器轉發在線用戶列表*/
struct Message3{
int Func_number; /*功能號: 此處取3 REFLESH_LIST
1------表示轉發信息
2------表示轉發更名通知
3------表示傳送當前用戶列表
4------表示客戶端離開
5------表示客戶加入
6------表示發送和接收文件
*/
int current_number; /* 當前在線人數 */
struct User user_list[MAX_CHATER];
/* 服務端維護的用戶列表 */
};
/*客戶端發出的離開信息*/
struct Message4{
int Func_number; /*功能號: 此處取4 CLIENT_LEFT
1------表示轉發信息
2------表示轉發更名通知
3------表示傳送當前用戶列表
4------表示客戶端離開
5------表示客戶加入
6------表示發送和接收文件
*/
char client_name[50]; /* 離開的客戶端名稱 */
};
/*新客戶登錄提示*/
struct Message5{
int Func_number; /*功能號: 此處取5 CLIENT_COME
1------表示轉發信息
2------表示轉發更名通知
3------表示傳送當前用戶列表
4------表示客戶端離開
5------表示客戶加入
6------表示發送和接收文件
*/
char username[50]; /*新客戶名稱*/
};
/*讀取文件塊大小*/
struct datablock{
unsigned char block[FILE_BLOCK]; /*讀取文件的塊大小*/
};
/*發送接收文件的數據結構*/
struct Message6{
int Func_number; /*功能號: 此處取6 SEND_RECV_FILE
1------表示轉發信息
2------表示轉發更名通知
3------表示傳送當前用戶列表
4------表示客戶端離開
5------表示客戶加入
6------表示發送和接收文件
*/
struct datablock msg; /*文件數據塊*/
char file_name[512]; /*文件名*/
int end_flag; /*文件結束標記
0------包含有文件信息
1------表示請求發送文件,要求建立連接
2------表示請求成功(對方同意接收文件)
3------表示請求不成功(對方不同意接收文件)
7------(END_OF_FILE)表示文件傳送完成
*/
char send_user_name [50]; /* 發送數據的用戶呢稱 */
char recv_user_name [50]; /* 接收數據的用戶名稱 */
};
/*
* Netchat 1.0 Server
*/
#include "head.h"
int main(int argc,char ** argv)
{
int listen_fd; /*define var for listen*/
int accept_fd[MAX_CHATER]; /*define fd for accept*/
int port_number; /*define port number*/
int total_connect_number; /*define var save total number*/
int i,sockaddr_in_size; /*define var for help run*/
struct sockaddr_in server_addr; /*define server addr*/
struct sockaddr_in client_addr; /*define client addr*/
struct Message3 allusers; /*define var to save user list*/
struct Message0 wellcome; /*define hello message*/
struct timeval wait_time; /*define time*/
pid_t pid;
fd_set read_fdset; /*define var for select*/
int max_fd; /*define var to save max fd*/
sockaddr_in_size=sizeof(struct sockaddr_in);
pid=-1;
/*init user list*/
memset(&allusers,0x0,sizeof(allusers));
allusers.Func_number=REFLESH_LIST;
memset(&wellcome,0x0,sizeof(wellcome));
wellcome.Func_number=SAY_HELLO;
/*send hello message*/
sprintf(wellcome.msg,"%s","Hello Wellcome to NETCHAT 1.0");
/*init time*/
wait_time.tv_sec=1;
wait_time.tv_usec=0;
/*check main arg*/
if(argc!=2)
{
fprintf(stderr,"\n[Error]\tWrong format,usage : %s [portnumber] to start server.\n",argv[0]);
exit(1);
}
if((port_number=atoi(argv[1]))<0)
{
fprintf(stderr,"\n[Error]\tWrong port number, the portnumber must bigger than zero."
"(suggest bigger than 5000)\n");
exit(1);
}
/*run server socket*/
if( (listen_fd=socket(AF_INET,SOCK_STREAM,0))==-1 )
{
fprintf(stderr,"\n[Error]\tSocket error:%s\n",strerror(errno));
exit(1);
}
/*fill server address*/
bzero(&server_addr,sizeof(struct sockaddr_in));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(port_number);
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
/*bind socket to listen_fd*/
if(bind(listen_fd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr_in))==-1)
{
fprintf(stderr,"\n[Error]\tBind error:%s\n",strerror(errno));
exit(1);
}
/*listen...*/
listen(listen_fd,MAX_CHATER);
fprintf(stderr,"\n-->The server had started successfully<--");
/*init accept_fd*/
memset(accept_fd,-1,MAX_CHATER);
max_fd=-1;
total_connect_number=0;
/*init userlist the total number*/
allusers.current_number=total_connect_number;
/*set listen_fd nonblock*/
fcntl(listen_fd,F_SETFL,O_NONBLOCK);
while(1)
{
int i;
char tmp_command[128];
fflush(stdout);
/*re-set read_fdset*/
FD_ZERO(&read_fdset);
/*add listen_fd to the read_fdset*/
FD_SET(listen_fd,&read_fdset);
if(listen_fd>max_fd)
max_fd=listen_fd;
/*add keybord input to read_fdset*/
FD_SET(0,&read_fdset);
/* readd all acceptfd to fdsett*/
for(i=0;i<total_connect_number;i++)
{
FD_SET(accept_fd[i],&read_fdset);
if(accept_fd[i]>max_fd)
max_fd=accept_fd[i];
}
/* wait_time.tv_sec=1;
wait_time.tv_usec=0;
*/
fprintf(stderr,"\n(Type \"exit\" and ENTER to exit)Enter command ->");
fflush(stdout);
/*do select*/
/* select(max_fd+1,&read_fdset,NULL,NULL,&wait_time); */
select(max_fd+1,&read_fdset,NULL,NULL,NULL);
if(FD_ISSET(0,&read_fdset))
{
char tmp_char;
int k=0;
tmp_char=getchar();
while(tmp_char!='\n')
{
tmp_command[k]=tmp_char;
k++;
tmp_char=getchar();
}
}
if(strcmp(tmp_command,"exit")==0)
goto exit_come;
if(FD_ISSET(listen_fd,&read_fdset))
{
/*accept a new connect*/
accept_fd[total_connect_number]=accept(listen_fd,
(struct sockaddr *)(&client_addr),&sockaddr_in_size);
/*add the new user to read_fdset*/
if(accept_fd[total_connect_number]!=-1)
{
int k;
time_t timep; /*define the second tmp*/
struct Message5 new_user;
memset(&new_user,0,sizeof(new_user));
new_user.Func_number=CLIENT_COME;
time(&timep);
/*become the username*/
sprintf(wellcome.username,"%s%d","chater",timep%10000);
strcpy(new_user.username,wellcome.username);
fprintf(stderr,"\n[TIP]\tA user connected");
send(accept_fd[total_connect_number],&wellcome,sizeof(wellcome),0);
for(k=0;k<total_connect_number;k++)
{
/*send wellcome message*/
send(accept_fd[k],&new_user,sizeof(new_user),0);
}
/*add the new connect fd to read_fdset*/
FD_SET(accept_fd[total_connect_number],&read_fdset);
/*re-value the max_fd*/
if(accept_fd[total_connect_number]>max_fd)
{
max_fd=accept_fd[total_connect_number];
}
sprintf( allusers.user_list[total_connect_number].user_name,
"%s",wellcome.username);
sprintf( allusers.user_list[total_connect_number].user_address,
"%s",inet_ntoa(client_addr.sin_addr) );
/*compute the new total number*/
allusers.current_number++;
total_connect_number++;
/*re-init the client address*/
memset(&client_addr,0x0,sizeof(client_addr));
}/*add accept_fd to fd_set end*/
continue;
}/*test new user come end*/
/*test is there are any message come*/
for(i=0;i<total_connect_number;i++)
{
/*if the message come*/
if(FD_ISSET(accept_fd[i],&read_fdset))
{
/*define var to receive the function number*/
int get_Func_number;
/*peek the function number*/
if(recv(accept_fd[i],&get_Func_number,sizeof(int),MSG_PEEK)
==-1)
{
fprintf(stderr,"\n[Error]\tReceive Function number "
"error:%s",strerror(errno));
get_Func_number=-1;
continue;
}
/*error happened ,exit*/
if(get_Func_number<=0)
{
fprintf(stderr,"The Function number is bad:%d\n",get_Func_number);
goto exit_come;
}
/*do by functin number*/
switch(get_Func_number)
{
/*transmit message to others*/
case 1:
{
/*define transmit var*/
struct Message1 get_message;
int j;
fprintf(stderr,"\n[TIP]\tThe get_Funcnumber is:%d",
get_Func_number);
/*receive message*/
if(recv(accept_fd[i],&get_message,
sizeof(get_message),0)==-1)
{
fprintf(stderr,
"\n[Error]\tReceive error:%s",
strerror(errno));
break;
}
/*do transmit*/
for(j=0;j<total_connect_number;j++)
{
send(accept_fd[j],&get_message,sizeof(get_message),0);
}
fprintf(stderr,"\n[TIP]\tTransmit1 (transmit message) message ok.");
break;
}
/*change name*/
case 2:
{
/*define var for change name*/
struct Message2 get_message;
int j;
memset(&get_message,0,sizeof(get_message));
fprintf(stderr,"\n[TIP]\tThe get_Funcnumber is:%d",get_Func_number);
/*receive change name message*/
if(recv(accept_fd[i],&get_message,sizeof(get_message),0)==-1)
{
fprintf(stderr,"\n[Error]\tReceive error:%s",strerror(errno));
break;
}
/*test name*/
for(j=0;j<total_connect_number;j++)
{
if(strcmp(allusers.user_list[j].user_name,get_message.newname)==0)
{
fprintf(stderr,"\n[Error]\tThe name had exist");
memset(get_message.newname,0,sizeof(get_message.newname));
strcpy(get_message.newname,"Error");
send(accept_fd[i],&get_message,sizeof(get_message),0);
fprintf(stderr,"\n[Error]\tChange name error");
goto exit_case_come;
}
}
/*change user list*/
for(j=0;j<total_connect_number;j++)
{
if(strcmp(allusers.user_list[j].user_name,get_message.oldname)==0)
{
memset(allusers.user_list[j].user_name,0,
sizeof(allusers.user_list[j].user_name));
strcpy(allusers.user_list[j].user_name,get_message.newname);
}
}
for(j=0;j<total_connect_number;j++)
{
send(accept_fd[j],&get_message,sizeof(get_message),0);
}
fprintf(stderr,"\n[TIP]\tTransmit2 (change name) message ok.");
break;
}
case 3:
/*transmit user list*/
{
/*define int var for receive function number*/
int get_message;
fprintf(stderr,"\n[TIP]\tThe get_Funcnumber is:%d",
get_Func_number);
/*receive message*/
if(recv(accept_fd[i],&get_message,
sizeof(int),0)==-1)
{
fprintf(stderr,
"\n[Error]\tReceive error:%s",
strerror(errno));
break;
}
fprintf(stderr,"\n[TIP]\tTotal user number is:%d",allusers.current_number);
/*do send user list*/
if(send(accept_fd[i],&allusers,sizeof(allusers),0) == -1)
{
fprintf(stderr,
"\n[Error]\tTransmit3 message error.");
break;
}
else
{
fprintf(stderr,
"\n[TIP]\tTransmit3 (send user list) "
"message ok.");
}
break;
}
/*transmit user left message*/
case 4:
{
/*define var for do*/
struct Message4 get_message;
int j,k;
fprintf(stderr,"\n[TIP]\tThe get_Funcnumber is:%d",get_Func_number);
/*receive message*/
if(recv(accept_fd[i],&get_message,sizeof(get_message),0)==-1)
{
fprintf(stderr,"\n[Error]\tReceive error:%s",strerror(errno));
break;
}
/*delete user from accept_fd[] and userlist[]*/
for(j=0;j<total_connect_number;j++)
{
if(strcmp(allusers.user_list[j].user_name,get_message.client_name)==0)
{
for(k=j;k<total_connect_number-1;k++)
{
allusers.user_list[k]=allusers.user_list[k+1];
}
for(k=j;k<total_connect_number-1;k++)
{
accept_fd[k]=accept_fd[k+1];
}
break;
}
}
total_connect_number--;
allusers.current_number--;
/*send all user the left message*/
for(j=0;j<total_connect_number;j++)
{
send(accept_fd[j],&get_message,sizeof(get_message),0);
}
fprintf(stderr,"\n[TIP]\tTransmit4 (a user had exit) message ok.");
break;
}/*end of case 4*/
/*receive file*/
case 6:
{
struct Message6 get_message;
int j;
fprintf(stderr,"\n[TIP]\tThe get_Funcnumber is:%d",get_Func_number);
memset(&get_message,0,sizeof(get_message));
if(recv(accept_fd[i],&get_message,sizeof(get_message),0)==-1)
{
fprintf(stderr,"\n[Error]\tReceive error:%s",strerror(errno));
break;
}
for(j=0;j<total_connect_number;j++)
{
/*find the dest accept_fd*/
if(strcmp(allusers.user_list[j].user_name,get_message.recv_user_name)==0)
{
break;
}
}
/*transmit the info*/
send(accept_fd[j],&get_message,sizeof(get_message),0);
break;
}
default:
{
fprintf(stderr,"\n[TIP]\tThe get_Funcnumber is:%d\n",get_Func_number);
goto exit_come;
}
exit_case_come:
}/*end of switch*/
}/*end of if*/
}/*end of for*/
}/*end of while*/
exit_come:
close(listen_fd);
exit(0);
}/*end of main*/