Android Monkey测试apk的开发

Monkey测试框架详解
本文介绍了一个基于Monkey的自动化测试框架实现细节,包括如何选择模块进行测试、配置测试时间和参数等。该框架支持忽略崩溃、超时等问题,并能记录详细的测试结果。

1.分享monkey测试apk的开发文档和内容。


package com.android.cw.monkeytest;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;


import android.R.string;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.Toast;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;


public class MainActivity extends Activity implements View.OnClickListener {


public static String TAG = "MonkeyRunningTest";
private PackageManager mPackageManager;
public int mIndex;
public Button mStartButton, resultButton, helpButton;
public CheckBox mSelectAllButton;
public EditText mEditText;
TextView resultTextView;
public HashMap<String, String> mCheckedModules;
private File file;
private CheckBox checkBox1, checkBox2, checkBox3, checkBox4;
private FileUtils fileUtils;
private String fileName1 = Environment.getExternalStorageDirectory()
+ "/monkey.txt";
private String whilelist = Environment.getExternalStorageDirectory()
+ "/whilelist.txt";
private static final String command1 = " logcat -v time ";
public Thread readsysLogThread;
Thread myThread;
public static boolean isreadsysLogThreadRun = false;


public static final String logfile2 = Environment
.getExternalStorageDirectory() + "/MonkeyTestResult.txt";


private boolean serviceisopen = false;// 服务是否开启
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
fileUtils = new FileUtils();


}


public void initData() {
mPackageManager = getPackageManager();
ListView listView = (ListView) findViewById(R.id.package_info_list);
mStartButton = (Button) findViewById(R.id.start_test);
resultButton = (Button) findViewById(R.id.btn_result);
helpButton = (Button) findViewById(R.id.btn_help);
resultTextView = (TextView) findViewById(R.id.tv_result);
mStartButton.setOnClickListener(this);
resultButton.setOnClickListener(this);
helpButton.setOnClickListener(this);
mSelectAllButton = (CheckBox) findViewById(R.id.select_all);
mEditText = (EditText) findViewById(R.id.time_edit);
checkBox1 = (CheckBox) findViewById(R.id.checkbox1);
checkBox2 = (CheckBox) findViewById(R.id.checkbox2);
checkBox3 = (CheckBox) findViewById(R.id.checkbox3);
checkBox4 = (CheckBox) findViewById(R.id.checkbox4);


PackageInfoAdapter adapter = new PackageInfoAdapter(this);
mCheckedModules = adapter.mCheckedMap;
listView.setAdapter(adapter);
ShellExecUtil util = new ShellExecUtil(this);


}


class PackageInfoAdapter extends BaseAdapter {
public Context mContext;
private List<PackageInfo> mPackageInfoList;
private List<CheckBox> mCheckBoxList;
public HashMap<String, String> mCheckedMap;
List<Integer> checkPosition;
PackageInfoAdapter(Context context) {
mContext = context;
mPackageInfoList = ShellExecUtil.getPakageNameList(context);
mCheckBoxList = new ArrayList<CheckBox>();
mCheckedMap = new HashMap<String, String>();
checkPosition = new ArrayList<Integer>();
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return ShellExecUtil.getPakageNameList(mContext).size();
}


@Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return arg0;
}


@Override
public long getItemId(int arg0) {
// TODO Auto-generated method stub
return arg0;
}


@Override
public View getView(int index, View convertView, ViewGroup root) {
// TODO Auto-generated method stub
ViewHolder viewHolder = null;
mIndex = index;
if (convertView == null) {
viewHolder = new ViewHolder();
convertView = LayoutInflater.from(mContext).inflate(
R.layout.activity_list, root, false);
viewHolder.name = (TextView) convertView
.findViewById(R.id.name);
viewHolder.packageName = (TextView) convertView
.findViewById(R.id.package_info);
viewHolder.checkBox = (CheckBox) convertView
.findViewById(R.id.checkbox);
Log.d(TAG, "index :=" + index);
viewHolder.checkBox.setTag(index);
convertView.setTag(viewHolder);
Log.d(TAG,
"checkBox1 getTag is " + viewHolder.checkBox.getTag());
} else {


viewHolder = (ViewHolder) convertView.getTag();
viewHolder.checkBox.setTag(index);
Log.d(TAG,
"checkBox2 getTag is " + viewHolder.checkBox.getTag());
}
if (checkPosition != null) {
Log.d(TAG, "checkPosition index is " + new Integer(index));
viewHolder.checkBox
.setChecked((checkPosition.contains(viewHolder.checkBox
.getTag()) ? true : false));
} else {
viewHolder.checkBox.setChecked(false);
}


// mCheckBoxList.add(checkBox);
viewHolder.name
.setText(mPackageInfoList.get(mIndex).applicationInfo
.loadLabel(mPackageManager));
viewHolder.packageName
.setText(mPackageInfoList.get(mIndex).packageName);


final CheckBox finalholder = viewHolder.checkBox;
viewHolder.checkBox
.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton button,
boolean isChecked) {
if (isChecked) {
if (!checkPosition.contains(finalholder
.getTag())) {
checkPosition.add((Integer) finalholder
.getTag());
}
mCheckedMap
.put(button.getTag().toString(),
mPackageInfoList.get(Integer
.parseInt(button
.getTag()
.toString())).packageName);
Log.d(TAG, "finalholder add Tag is "
+ (Integer) finalholder.getTag());
} else {
if (checkPosition.contains(finalholder.getTag())) {
checkPosition.remove(finalholder.getTag());
}
mCheckedMap.remove(button.getTag().toString());
Log.d(TAG, "finalholder remove Tag is "
+ (Integer) finalholder.getTag());
}

ShellExecUtil.setPackageNameMap(mCheckedMap);
}
});
return convertView;
}
}


class ViewHolder {
TextView name;
TextView packageName;
CheckBox checkBox;
}
private String getCheckBoxcommand() {
StringBuilder stringCheck = new StringBuilder();
if (checkBox1.isChecked()) {
stringCheck.append(" --ignore-crashes ");
}
if (checkBox2.isChecked()) {
stringCheck.append(" --ignore-timeouts ");
}
if (checkBox3.isChecked()) {
stringCheck.append(" --ignore-security-exceptions ");
}
if (checkBox4.isChecked()) {
stringCheck.append(" --ignore-native-crashes ");
}


return stringCheck.toString();


}


@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.start_test :
mStartButton.setEnabled(false);
startReadLogThread();


getCheckBoxcommand();
if ((mCheckedModules.size() == 0)
&& (!mSelectAllButton.isChecked())) {
Toast.makeText(this, "please choose test modoules",
Toast.LENGTH_LONG).show();
} else {
try {
Log.d(TAG,
"mEditText.getText() is  "
+ mEditText.getText());
if (TextUtils.isEmpty(mEditText.getText())) {
Toast.makeText(this,
"Default test time is 3000000!!!",
Toast.LENGTH_SHORT).show();
} else {
ShellExecUtil.setTestTimes(Long.parseLong(mEditText
.getText().toString()));
}


file = new File(whilelist);
if (file.exists()) {
file.delete();
file.createNewFile();
} else {
file.createNewFile();
}
OutputStream os = new FileOutputStream(file, true);
Iterator iter = mCheckedModules.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
String mPackageName = (String) entry.getValue()
+ "\r\n";
os.write(mPackageName.getBytes());
}


mRunMonkeyTestCase.start();
if (os != null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
break;
case R.id.btn_result :
resultButton.setEnabled(false);
if (readsysLogThread != null) {
readsysLogThread.interrupt();
}
UpdateUI();
break;


case R.id.btn_help :
Intent helpIntent = new Intent(MainActivity.this,
HelpActvity.class);
startActivity(helpIntent);
break;
}


}


public void UpdateUI() {
myThread = new Thread(new Runnable() {
@Override
public void run() {
try {
fileUtils.readFileByLines(logfile2, mHandler);


Thread.sleep(2000);
Message message = new Message();
message.what = 1;
mHandler.sendMessage(message);


} catch (Exception e) {
e.printStackTrace();
}


}
});
myThread.start();
}


public Handler mHandler = new Handler() {
@Override
public void handleMessage(android.os.Message msg) {
super.handleMessage(msg);
String str = fileUtils.sBuilder.toString();
if (msg.what == 1) {
resultTextView.setText("--测试结果---" + "\n" + str);
}


};
};


/**
* 开启抓log线程
*/
private void startReadLogThread() {
readsysLogThread = new Thread(new Runnable() {
public void run() {
// 设置开关让他停止
try {
ShellExecUtil.execCommand(command1);


} catch (Exception e) {
e.printStackTrace();
}


}
});
readsysLogThread.start();
}


public Thread mRunMonkeyTestCase = new Thread(new Runnable() {


@Override
public void run() {
try {


ShellExecUtil.execCommand(ShellExecUtil.getCommond(file,
getCheckBoxcommand(), mSelectAllButton.isChecked()));
// ShellExecUtil.execCommand("monkey --throttle 1000 --ignore-crashes --ignore-timeouts --ignore-security-exceptions --ignore-native-crashes --monitor-native-crashes --kill-process-after-error -v -v -v 1000 > /storage/sdcard0/monkey.txt");


} catch (Exception e) {
e.printStackTrace();
}


}
});


protected void onDestroy() {
super.onDestroy();


if (mRunMonkeyTestCase != null) {
mRunMonkeyTestCase.interrupt();
}
if (readsysLogThread != null) {
readsysLogThread.interrupt();
}
if (myThread != null) {
myThread.interrupt();
}
};


}


第二个类:

package com.android.cw.monkeytest;


import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.List;


import android.R.integer;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.nfc.Tag;
import android.os.Environment;
import android.telephony.TelephonyManager;
import android.util.Log;


public class ShellExecUtil {


public static HashMap<String, String> mPackageName;
public static long mTimes = 3000000;
public static Context mContext;
 public static final String logcatfile= Environment.getExternalStorageDirectory()+"/MonkeyTestResult.txt";
public static  StringBuilder sBuilder=null;
public ShellExecUtil(Context context) {
// TODO Auto-generated constructor stub
mContext = context;
}
public static void setPackageNameMap(HashMap<String, String> packageName) {
mPackageName = packageName;
}


public static void setTestTimes(long times) {
mTimes = times;
}


@SuppressLint("SdCardPath")
public static String getCommond(File whilelist, String stringcommand,
boolean isAllModules) {
StringBuilder commond = new StringBuilder();
if (isAllModules) {
commond.append("monkey");
} else {
commond.append("monkey --pkg-whitelist-file ").append(
whilelist.getPath());
}
commond.append(" --throttle 1000 ")
.append(stringcommand)
// .append("--ignore-crashes --ignore-timeouts --ignore-security-exceptions --ignore-native-crashes")
.append(" --monitor-native-crashes --kill-process-after-error -v -v -v ")
.append(Long.toString(mTimes));
//Log.i("", "----commond->" + commond.toString());
return commond.toString();
}


public static void execCommand(String command) {
sBuilder = new StringBuilder();
int count = 1;// 次数
Process proc = null;
try {
Runtime runtime = Runtime.getRuntime();
proc = runtime.exec(command);
BufferedReader in = new BufferedReader(new InputStreamReader(
proc.getInputStream()));
StringBuffer stringBuffer = new StringBuffer();
String line = null;
File file=new File(logcatfile);

FileWriter fr = new FileWriter(file);

while ((line = in.readLine()) != null) {

fr.write(line+"\r\n");
}

fr.close();


} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(proc!=null){
proc.destroy();
}

} catch (Exception e2) {
e2.printStackTrace();
}
}
}


public static List<PackageInfo> getPakageNameList(Context context) {
PackageManager packageManager = context.getPackageManager();


List<PackageInfo> list = packageManager
.getInstalledPackages(PackageManager.GET_PERMISSIONS);
return list;
}


public static String getIMEI() {
String im = "";
TelephonyManager mTelephonyManager = (TelephonyManager) mContext
.getSystemService(Context.TELEPHONY_SERVICE);
im = mTelephonyManager.getDeviceId();
if (im == null || im.trim().length() == 0) {
im = getMacAddress();
}
return im;
}


/**
* 获取mac地址
* @return
*/
public static String getMacAddress() {
WifiManager wifi = (WifiManager) mContext
.getSystemService(Context.WIFI_SERVICE);
WifiInfo info = wifi.getConnectionInfo();
String mac = "";
try {
if (info != null) {
mac = info.getMacAddress();
mac = mac.replaceAll(":", "-");
}
} catch (Exception e) {
// TODO: handle exception
}


return mac;
}


}


第三个类:文件操作类

package com.android.cw.monkeytest;


import android.R.integer;
import android.content.Context;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;


import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;


public class FileUtils {
public static StringBuilder sBuilder;


public FileUtils() {
super();

}
public static void writeLogFile(Context mcontext, String fileName,
String content, Boolean flag) {
File file = new File(mcontext.getExternalFilesDir("log"), fileName);// getExternalCacheDir()
if (file.exists() && flag) {
file.delete();
}
FileOutputStream fos;
try {
fos = new FileOutputStream(file, true);
fos.write((content).getBytes());
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void writeLogFile(Context mcontext, String fileName,
String content) {
writeLogFile(mcontext, fileName, content, false);
}


public static String getSDPath() {
File sdDir = null;
boolean sdCardExist = Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED); // 判断sd卡是否存在
if (sdCardExist) {
sdDir = Environment.getExternalStorageDirectory();// 获取跟目录
}
return sdDir.toString();
}
public static void method4(Context context, String file, String content) {
// 每次写入文件前先删除原有的文件
File file1 = new File(file);
if (file1.exists()) {
file1.delete();
}
FileOutputStream fos;
try {
fos = new FileOutputStream(file, true);
fos.write((content).getBytes());
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}


/**
* 以行为单位读取文件,常用于读面向行的格式化文件
*/
public void readFileByLines(String fileName, Handler mHandler) {


sBuilder=new StringBuilder();
File file = new File(fileName);
if (!file.exists()) {
Log.i("", "---文件未找到---");
return;
}
BufferedReader reader = null;
try {
System.out.println("---以行为单位读取文件内容,一次读一整行:");
reader = new BufferedReader(new FileReader(file));
String tempString = null;
int line = 1;
int count=0;
int count1 = 0;// 次数
int count2 = 0;// 次数
int count3 = 0;// 次数
int count4 = 0;// 次数
int count5 = 0;// 次数
int count6 = 0;// 次数
int count7 = 0;// 次数
// 一次读入一行,直到读入null为文件结束
while ((tempString = reader.readLine()) != null) {
// 显示行号
//System.out.println("--line " + line + ": " + tempString);
// line++;


if (tempString.contains("Crash")|| (tempString.contains("crash"))) {
count1++;
sBuilder.append("crash出现的次数:" + count1 + "\n" + tempString
+ "\n");

}
if (tempString.contains("anr") || tempString.contains("ANR")||tempString.contains("Anr")) {
count2++;
sBuilder.append("anr出现的次数:" + count2 + "\n" + tempString
+ "\n");
}
if (tempString.contains("Tombstone")) {
count3++;
sBuilder.append("tombstone出现的次数:" + count3 + "\n"
+ tempString + "\n");
}
if (tempString.contains("Fatal")) {
count4++;
sBuilder.append("Fatal出现的次数:" + count4 + "\n" + tempString
+ "\n");
}


if (tempString.contains("Watchdog")||tempString.contains("watchdog")) {
count5++;
sBuilder.append("Watchdog出现的次数:" + count5 + "\n"
+ tempString + "\n");
}
if (tempString.contains("vmreboot")
|| tempString.contains("VmReboot")) {
count6++;
sBuilder.append("vmreboot出现的次数:" + count6 + "\n"
+ tempString + "\n");
}
if (tempString.contains("powercollapse")
|| tempString.contains("PowerCollapse")) {
count7++;
sBuilder.append("powercollapse出现的次数:" + count7 + "\n"
+ tempString + "\n");
}
// Log.i("", "-----读完------>" + sBuilder.toString());
//return sBuilder.toString();
}
//UpdateUI(mHandler);
// Log.i("", "-----结果分析------>" + sBuilder.toString());
reader.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
public void UpdateUI(final Handler mHandler) {
Thread myThread = new Thread(new Runnable() {


@Override
public void run() {
try {

Message message = new Message();
message.what = 1;
mHandler.sendMessage(message);


} catch (Exception e) {
e.printStackTrace();
}


}
});
myThread.start();
}


}


main布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
   >




    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >


        <Button
            android:id="@+id/start_test"
            android:layout_width="0dip"
            android:layout_height="50dp"
            android:layout_alignParentBottom="true"
            android:layout_weight="1"
            android:text="@string/start_test"
            android:textColor="#f00" />


        <Button
            android:id="@+id/btn_result"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_alignParentBottom="true"
            android:layout_weight="1"
            android:text="结果"
            android:textColor="#f00" />


        <Button
            android:id="@+id/btn_help"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_alignParentBottom="true"
            android:layout_weight="1"
            android:text="帮助"
            android:textColor="#f00" />
    </LinearLayout>
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollbars="vertical" >


        <TextView
            android:id="@+id/tv_result"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/tv_result" />
    </ScrollView>


    <CheckBox
        android:id="@+id/checkbox1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:checked="false"
        android:text="@string/checkbox1" />


    <CheckBox
        android:id="@+id/checkbox2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/checkbox2" />


    <CheckBox
        android:id="@+id/checkbox3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/checkbox3" />


    <CheckBox
        android:id="@+id/checkbox4"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/checkbox4" />




    <LinearLayout
        android:id="@+id/printitle"
        android:layout_width="match_parent"
        android:layout_height="20dp"
        android:layout_alignParentTop="true" >


        <TextView
            android:id="@+id/modoule_name"
            android:layout_width="80dp"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:ellipsize="marquee"
            android:layout_weight="1"
            android:focusableInTouchMode="true"
            android:marqueeRepeatLimit="marquee_forever"
            android:singleLine="true"
            android:text="@string/modoule_name" />


        <View
            android:id="@+id/sepline"
            android:layout_width="20dp"
            android:layout_height="match_parent"
            android:layout_toRightOf="@id/modoule_name" />


        <TextView
            android:id="@+id/package_name"
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:layout_centerVertical="true"
            android:layout_toRightOf="@id/sepline"
            android:ellipsize="marquee"
            android:focusableInTouchMode="true"
            android:marqueeRepeatLimit="marquee_forever"
            android:singleLine="true"
            android:text="@string/package_name" />


        <TextView
            android:id="@+id/select_state"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:layout_weight="1"
            android:layout_marginRight="2dp"
            android:text="@string/select_state" />
    </LinearLayout>




    <LinearLayout
        android:id="@+id/times_containner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="1dip" >


        <TextView
            android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_weight="1"
            android:text="@string/test_times" />


        <EditText
            android:id="@+id/time_edit"
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:layout_weight="3"
            android:text="@null" />


        <CheckBox
            android:id="@+id/select_all"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="right"
            android:layout_alignParentRight="true"
            android:layout_marginRight="0dp"
            android:text="@string/select_all"
          />


    </LinearLayout>
    <!-- android:background="@drawable/button" -->


    <ListView
        android:id="@+id/package_info_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@id/times_containner"
        android:layout_below="@id/printitle" >
    </ListView>


</LinearLayout>

list.xml文件:

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


    <TextView
        android:id="@+id/name"
        android:layout_width="80dp"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true" 
        android:ellipsize="marquee"    
        android:marqueeRepeatLimit="marquee_forever" 
        android:focusableInTouchMode="true"
       />


    <View
        android:id="@+id/sep_line"
        android:layout_width="20dp"
        android:layout_height="match_parent"
        android:layout_toRightOf="@id/name" />


    <TextView
        android:id="@+id/package_info"
        android:layout_width="250dp"
        android:layout_height="wrap_content"
        android:singleLine="true" 
        android:ellipsize="marquee"    
        android:focusableInTouchMode="true"
        android:marqueeRepeatLimit="marquee_forever" 
        android:layout_centerVertical="true"
        android:layout_toRightOf="@id/sep_line" />


    <CheckBox
        android:id="@+id/checkbox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:layout_marginRight="0dp" />


</RelativeLayout>



需要添加权限:

android:sharedUserId="android.uid.system"

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值