调节调试窗口字体:
https://blog.csdn.net/qq_32452623/article/details/52403725
1、单元测试(未代码验证)
报出如下错误日志打印:
java.lang.RuntimeException: Method setUp in android.test.AndroidTestCase not mocked. See http://g.co/androidstudio/not-mocked for details.
at android.test.AndroidTestCase.setUp(AndroidTestCase.java)
at junit.framework.TestCase.runBare(TestCase.java:139)
at junit.framework.TestResult$1.protect(TestResult.java:122)
at junit.framework.TestResult.runProtected(TestResult.java:142)
at junit.framework.TestResult.run(TestResult.java:125)
at junit.framework.TestCase.run(TestCase.java:129)
at junit.framework.TestSuite.runTest(TestSuite.java:252)
at junit.framework.TestSuite.run(TestSuite.java:247)
at org.junit.internal.runners.JUnit38ClassRunner.run(JUnit38ClassRunner.java:86)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMainV2.main(AppMainV2.java:131)
Process finished with exit code -1
2、存储方式
ram:运行内存
rom:内部存储 /data/data/package_name
sd: 外部存储
3、QQ登录案例(本地存储 文件存储密码)
private void writeData() {
//用文件存储密码
try {
File file = new File("/data/data/com.gordon.junit/cache/pwd.txt");
FileOutputStream fos = new FileOutputStream(file);
//账号和密码
String userName = "gordon";
String passWord = "****";
fos.write((userName+"&"+passWord).getBytes());
fos.close();
Toast.makeText(this,"success",Toast.LENGTH_SHORT).show();
} catch (FileNotFoundException e) {
e.printStackTrace();
Toast.makeText(this,"FileNotFoundException",Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
Toast.makeText(this,"IOException",Toast.LENGTH_SHORT).show();
}
}
读取数据
private void readData() {
File file = new File("/data/data/com.gordon.junit/cache/pwd.txt");
API可替换File file = new File(this.getCacheDir(),"pwd.txt");
API可替换File file = new File(getFilesDir(),"pwd.txt");
try {
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
String s = br.readLine();
String[] split = s.split("&");
Toast.makeText(this,"userName"+split[0],Toast.LENGTH_SHORT).show();
br.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
输入框密文显示密码
4、context 上下文
文件读写权限
5、QQ登录之API方式
读文件API
文件默认在files目录下
public void qqLoginRead() {
try {
FileInputStream fis = this.openFileInput("config.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
String s = br.readLine();
String[] split = s.split("&");
String userName = split[0];
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
写文件API
public void qqLoginWrite() {
try {
FileOutputStream fos = this.openFileOutput("config.txt", 0);
//账号和密码
String userName = "gordon";
String passWord = "****";
fos.write((userName+"&"+passWord).getBytes());
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
6、QQ登录之流方式
文件在cache目录下
API可替换File file = new File(this.getCacheDir(),"pwd.txt");
文件在files目录下
API可替换File file = new File(getFilesDir(),"pwd.txt");
7、QQ登录之SD卡存储方式
写SD卡
public void sdCardWrite() {
File file = new File("/mnt/sdcard/waibu.txt");
try {
FileOutputStream fos = new FileOutputStream(file);
String userName = "gordon";
String passWord = "****";
fos.write((userName+"&"+passWord).getBytes());
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
读SD卡
public void sdCardRead() {
File file = new File("/mnt/sdcard/waibu.txt");
try {
FileInputStream fis = new FileInputStream(file);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
String s = br.readLine();
String[] split = s.split("&");
String userName = split[0];
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
读写SD卡时需要在AndroidManifest.xml文件加权限
8、SD卡使用问题
1 判断是否有sd卡
public void checkSdCard() {
String state = Environment.getExternalStorageState();
if(Environment.MEDIA_MOUNTED.equals(state)) {
//插入了SD卡
}else if(Environment.MEDIA_UNMOUNTED.equals(state)) {
//拔出了SD卡
}
}
2 sd卡路径不是/mnt/sdcard 或者是/storage
public void getSdCardAddress() {
File file = Environment.getExternalStorageDirectory();
String address = file.getAbsolutePath();
}
3 可用空间等
public void getSdCardFreeSpace() {
File file = Environment.getExternalStorageDirectory();
long freeSpace = file.getFreeSpace();
//格式化大小
String formatFileSize = Formatter.formatFileSize(this, freeSpace);
}
9、QQ登录之SharedPreference
SharedPreference文件保存目录在/data/data/package_name/shared_
prefs
写文件:底层是map集合的方式存储数据
/**
* 1 获取sp
* 2 拿到编辑器
* 3 设置要存储的数据
* 4 提交
*/
public void qqLoginSPWrite() {
SharedPreferences sp = this.getSharedPreferences("config", 0);
SharedPreferences.Editor edit = sp.edit();
edit.putString("qq","5057");
edit.putString("pwd", "5057");
edit.commit();
}
读文件:
public void qqLoginSPRead() {
SharedPreferences sp = this.getSharedPreferences("config", 0);
sp.getString("qq","123");
}
用途:
1 存储密码 配置信息
2 自动添加后缀.xml
10、生成xml和解析
特点:
1 用标签存储数据
2 跨平台读取数据
生成xml文件:
/**
* 1 创建序列化器
* 2 设置参数
* 3 生成文件
*/
public void xmlWrite() {
XmlSerializer serializer = Xml.newSerializer();
try {
FileOutputStream fos = this.openFileOutput("student.xml", 0);
serializer.setOutput(fos,"UTF-8");
//生成xml文件
serializer.startDocument("UTF-8", true);
//根标签
serializer.startTag(null, "stu");
//姓名
serializer.startTag(null, "name");
serializer.text("gordon");
serializer.endTag(null, "name");
//学号
serializer.startTag(null, "number");
serializer.text("110");
serializer.endTag(null, "number");
serializer.endTag(null, "stu");
serializer.endDocument();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
解析xml文件:
解析xml文件的方式:
1 sax基于事件
2 dom&dom4j 用dom树解析
3 pull类似sax(效率高) android使用
/**
* 1 创建解析器
* 2 设置参数
* 3 解析
*/
public void xmlRead() {
try {
XmlPullParser parser = Xml.newPullParser();
FileInputStream fis = null;
fis = openFileInput("student.xml");
parser.setInput(fis,"UTF-8");
int type = parser.getEventType();
String name = null;
String number = null;
while (type != XmlPullParser.END_DOCUMENT) {
String tag = parser.getName();
switch (type){
case XmlPullParser.START_TAG:
if("name".equals(tag)) {
name = parser.nextText();
System.out.println(name);
}else if("number".equals(tag)) {
number = parser.nextText();
System.out.println(number);
}
break;
default:
break;
}
type = parser.next();
}
fis.close();
//在此view设置数据
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
11、帧动画
12、国际化
13、Android数据存储方式 5种(按照数据存储的位置划分)
14、Sqlite数据库
轻量级、开源的、微小型数据库
创建文件:
public void createFiles() throws IOException {
//1 在内存中创建一个指向d盘的文件对象
File file = new File("D:\\file.txt");
//2 在硬盘上创建文件并写入内容
FileOutputStream fos = new FileOutputStream(file);
fos.write("hello".getBytes());
fos.close();
}
创建数据库:
public class MyDbOpenHelper extends SQLiteOpenHelper{
public MyDbOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
public MyDbOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version, DatabaseErrorHandler errorHandler) {
super(context, name, factory, version, errorHandler);
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
1 创建一个类MyDbOpenHelper 继承 SQLiteOpenHelper
2 重写其构造方法
public MyDbOpenHelper(Context context) {
super(context, "student.db", null, 1);
}
3 在view中调用
1 在内存中创建数据库帮助类的对象
MyDbOpenHelper helper = new MyDbOpenHelper(this);
2 在磁盘上创建数据库文件
helper.getWritableDatabase();
此时会在package_name/databases/下生成 student.db和student.db-journal两个文件
4 创建数据库表
第一次创建数据库的时候调用,适合初始化数据库的表
不检查类型和长度如:name varchar(20)
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table stu(_id integer primary key autoincrement, name varchar(20), number varchar(20))");
}
只有当数据库版本升级后才会调用以下方法
如将版本号变成2
public MyDbOpenHelper(Context context) {
super(context, "student.db", null, 2);
}
此时会调用如下方法
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
可修改数据库的表,如增加列
}
问题:
1、此数据库不能降级 3->1,会出错
2、数据库版本号必须>=1(无效的参数异常)
5 操作数据库表(增、删、改、查)
sql语句:
增
insert into stu(‘name’, ‘name’) values(‘林青霞’, ‘100’)
查
select * from stu
改
update stu set name=‘liuyifei’ while
删
delete from stu while id=1
public void insert() {
MyDbOpenHelper helper = new MyDbOpenHelper(this);
SQLiteDatabase db = helper.getWritableDatabase();
db.execSQL("insert into stu('name', 'number') values (?, ?)", new Object[]{"gordon", "110"});
db.close();
}
public void delete() {
MyDbOpenHelper helper = new MyDbOpenHelper(this);
SQLiteDatabase db = helper.getWritableDatabase();
db.execSQL("delete from stu");
db.close();
}
public void update() {
MyDbOpenHelper helper = new MyDbOpenHelper(this);
SQLiteDatabase db = helper.getWritableDatabase();
db.execSQL("update stu set name=?", new String[]{"gordon1"});
db.close();
}
public void query() {
MyDbOpenHelper helper = new MyDbOpenHelper(this);
SQLiteDatabase db = helper.getWritableDatabase();
Cursor cursor = db.rawQuery("select * from stu", null);
while (cursor.moveToNext()) {
String _id = cursor.getString(0);//0: _id 1: name
}
db.close();
}
6 api操作数据库:
public void apiInsert() {
MyDbOpenHelper helper = new MyDbOpenHelper(this);
SQLiteDatabase db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("name", "xiaoshen");
long id = db.insert("stu", null, values);
if (id == -1) {
//插入失败
}else {
}
db.close();
}
public void apiDelete() {
MyDbOpenHelper helper = new MyDbOpenHelper(this);
SQLiteDatabase db = helper.getWritableDatabase();
int id = db.delete("stu", null, null);
if(id == 0) {
//删除失败
}else {
}
db.close();
}
public void apiUpdate() {
MyDbOpenHelper helper = new MyDbOpenHelper(this);
SQLiteDatabase db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("name", "nimei");
int id = db.update("stu", values, null, null);
if(id == 0) {
//更新失败
}else {
}
db.close();
}
/**
* table 表名
* columns 要查询的列
* selection 查询条件
* selectionArgs 查询条件的占位符
* groupBy 分组
* having 条件
* orderBy 排序
*/
public void apiQuery() {
MyDbOpenHelper helper = new MyDbOpenHelper(this);
//加锁线程安全
SQLiteDatabase db = helper.getWritableDatabase();
//未加锁效率高
//SQLiteDatabase readDb = helper.getReadableDatabase();
Cursor cursor = db.query("stu", new String[]{"name", "num", "_id"}, null, null, null, null, null);
while (cursor.moveToNext()) {
String _id = cursor.getString(0);//0: _id 1: name
}
db.close();
}
7 操作命令行进行增删改查
cmd
8 数据库事务
修改数据库要么同时成功,要么同时失败
public void shiwu() {
MyDbOpenHelper helper = new MyDbOpenHelper(this);
SQLiteDatabase db = helper.getWritableDatabase();
try {
db.beginTransaction();
ContentValues values = new ContentValues();
values.put("name", 0+1000000);
db.update("stu", values, "name=?", new String[]{"kehu"});
ContentValues values1 = new ContentValues();
values1.put("name", 1000000-1000000);
db.update("stu", values1, "name=?", new String[]{"yinhang"});
db.setTransactionSuccessful();
}catch (Exception e){
e.printStackTrace();
}finally {
db.endTransaction();
}
db.close();
}
15、ListView
16、网络编程
HTTP协议:
GET
Request URL:http://localhost:8080/web/LoginServlet?qq=123&pwd=asd
Request Method:GET
Status Code:200 OK
ccept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip, deflate, sdch
Accept-Language:zh-CN,zh;q=0.8
Connection:keep-alive
Cookie:JSESSIONID=63630741907F82A937A9C96CCB43868E
Host:localhost:8080
Referer:http://localhost:8080/web/login.jsp
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36
POST
Request URL:http://localhost:8080/web/LoginServlet
Request Method:POST
qq=123&pwd=ASD
Status Code:200 OK
Remote Address:[::1]:8080
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip, deflate
Accept-Language:zh-CN,zh;q=0.8
Cache-Control:max-age=0
Connection:keep-alive
Content-Length:14
Content-Type:application/x-www-form-urlencoded
Cookie:JSESSIONID=63630741907F82A937A9C96CCB43868E
Host:localhost:8080
Origin:http://localhost:8080
Referer:http://localhost:8080/web/login.jsp
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36
1 打开tomcat服务器
2 webapps目录下zhbj查看图片
http://192.168.91.1:8080/zhbj/10008/1452327318UU92.jpg
3 点击检查->Network
4 访问一个网页
5 查看其请求头等信息
17、网络通信
1 写一个url
2 用这个url打开http连接
3 设置请求参数
4 获取状态码
2xx 响应成功 3xx重定向 4xx资源错误 5xx服务器错误
5 获取服务器返回的二进制输入流
注:在AndroidManifest.xml 添加网络权限
直接操作流访问网络
public void openUrl() {
try {
//1
URL url = new URL("https://www.baidu.com");
//2 打开http
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
//3 设置请求参数(默认是get)
conn.setRequestMethod("GET");
conn.setConnectTimeout(3000);
//4 获取状态码
int code = conn.getResponseCode();
if(code == 200) {
//5 获取服务器返回的二进制输入流
InputStream is = conn.getInputStream();
//6 把流转换为位图对象
Bitmap bitmap = BitmapFactory.decodeStream(is);
//7 显示在view
}else {
//请求失败
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
图片内存大小计算:
宽高4=12808004
在windows下大小为603kb
android4.4在主线程操作网络异常(2.3.2无)
获取当前线程名
String currentThreadName = Thread.currentThread().getName();
开启子线程
new Thread() {
@Override
public void run() {
super.run();
}
}.start();
18、消息机制编写步骤
//1 在主线程中创建Handler
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//3 在handlerMessage()中修改UI
switch (msg.what) {
case 1:
String s = String.valueOf(msg.obj);
break;
default:
break;
}
}
};
public void start() {
new Thread() {
@Override
public void run() {
super.run();
for (int i = 0; i < 101; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//2 在子线程中用handler发消息
Message message = new Message();
message.obj = i;
mHandler.sendMessage(message);
}
}
}.start();
}
19、将流转换为文字(操作字符串–工具类—01)
public class StringUtils {
/**
* 从流转换为字符串
* @param is 输入流
* @return null 失败
*/
public static String parseStream2Str(InputStream is) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len = -1;
byte[] buffer = new byte[1024*8];
String str = null;
try {
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
str = new String(baos.toByteArray());
}
return str;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
对比:
//2.0 在子线程中用handler发消息
Message message = new Message();
message.obj = i;
mHandler.sendMessage(message);
//2.1 从消息池中获取消息(效率高)
Message msg = Message.obtain();
msg.obj = "string";
msg.what = 2;(区别哪个发送的)
mHandler.sendMessage(msg);
Handler MessageQueue Message Looper关系:
任何带有界面的操作系统都运行在一个死循环中
用户操作时,用Handler发送一个消息给系统的MessageQueue消息队列,所有的消息在消息队列中排队,looper轮询器不断从消息队列中取出消息,给Handler的HandlerMessage方法,然后在这个方法中更新ui
20、Handler之api方式
public class ToastUtils {
public void showToastInAnyThread(final Activity context, final String msg) {
context.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
});
}
}
21、常见数据适配器
public void setArrayAdapter() {
/**
* 1 context
* 2 listview 条目布局文件
* 3 参2布局文件中的控件
* 4 string数组,data
*/
lvArrayAdapter.setAdapter(new ArrayAdapter(MainActivity.this, R.layout.array_adapter_list_item, R.id.tv_text, str));
}
22、复杂ListView
23、访问网络
在values目录下创建一个config.xml文件
http://www.baidu.com
网络请求工具类
public class NetService {
public static List requestNetwork(Context context) {
try {
String path = context.getResources().getString(R.string.networkIp);
//1
// URL url = new URL("https://www.baidu.com");
URL url = new URL(path);
//2 打开http
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
//3 设置请求参数(默认是get)
conn.setRequestMethod("GET");
conn.setConnectTimeout(3000);
//4 获取状态码
int code = conn.getResponseCode();
if(code == 200) {
//5 获取服务器返回的二进制输入流
InputStream is = conn.getInputStream();
//解析xml文件
//7 显示在view
}else {
//请求失败
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
24、展示图片
SmartImageView(开源框架)
https://github.com/JackCho/SmartImageView
25、自定义控件 SmartImageView
package widget;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* author: Gordon
* created on: 2017/12/5 23:11
* description:
*/
public class SmartImageView extends android.support.v7.widget.AppCompatImageView {
private static final int MSG_SUCC = 0;
private static final int MSG_ERR = 1;
private static final int MSG_ERR_CODE = 2;
public SmartImageView(Context context) {
super(context);
}
public SmartImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public SmartImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MSG_SUCC:
//7 显示在view(在主线程中)
setImageBitmap((Bitmap) msg.obj);
break;
case MSG_ERR:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_foreground);
setImageBitmap(bitmap);
break;
case MSG_ERR_CODE:
break;
}
}
};
public void setImageURL(final String path) {
new Thread() {
@Override
public void run() {
super.run();
//获取网络数据
URL url = null;
try {
url = new URL(path);
//2 打开http
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
//3 设置请求参数(默认是get)
conn.setRequestMethod("GET");
conn.setConnectTimeout(3000);
//4 获取状态码
int code = conn.getResponseCode();
if(code == 200) {
//5 获取服务器返回的二进制输入流
InputStream is = conn.getInputStream();
//6 把流转换为位图对象
Bitmap bitmap = BitmapFactory.decodeStream(is);
Message message = Message.obtain();
message.obj = bitmap;
message.what = MSG_SUCC;
mHandler.sendMessage(message);
//7 显示在view(在主线程中)
}else {
//请求失败
Message errorMessage = Message.obtain();
errorMessage.obj = code;
errorMessage.what = MSG_ERR_CODE;
mHandler.sendMessage(errorMessage);
}
} catch (Exception e) {
e.printStackTrace();
mHandler.sendEmptyMessage(MSG_ERR);
}
}
}.start();
}
}
26、http之get/post请求提交数据到服务器
/**
* GET:
*/
private void requestNetWorkByGet(String userName, String passWord) {
String path = "http://192.168.3.100:8080/web/Login?userName=+" + userName + "&passWord" + passWord;
//获取网络数据
URL url = null;
try {
url = new URL(path);
//2 打开http
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
//3 设置请求参数(默认是get)
conn.setRequestMethod("GET");
conn.setConnectTimeout(3000);
conn.setReadTimeout(3000);
//4 获取状态码
int code = conn.getResponseCode();
if(code == 200) {
//5 获取服务器返回的二进制输入流
InputStream is = conn.getInputStream();
String textByGet = StringUtils.parseStream2String(is);
//handler主线程显示数据
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* POST:
*/
private void requestNetWorkByPost(String userName, String passWord) {
String path = "http://192.168.3.100:8080/web/Login";
//获取网络数据
URL url = null;
try {
url = new URL(path);
//2 打开http
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
//3 设置请求参数(默认是get)
conn.setRequestMethod("POST");
conn.setConnectTimeout(3000);
conn.setReadTimeout(3000);
//3.1 多了两个请求头
String text = "userName=" + userName + "&passWord = " + passWord;
conn.setRequestProperty("Content-Length", String.valueOf(text.length()));
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
//3.2 用二进制输入流提交数据
conn.setDoOutput(true);//声明要给服务器提交数据了
conn.getOutputStream().write(text.getBytes());
//4 获取状态码
int code = conn.getResponseCode();
if(code == 200) {
//5 获取服务器返回的二进制输入流
InputStream is = conn.getInputStream();
String textByPost = StringUtils.parseStream2String(is);
//handler主线程显示数据
}
} catch (Exception e) {
e.printStackTrace();
}
}
27、中文乱码问题
客户端和服务端所用的编码方式不同
28、开源框架httpclient向服务器提交数据
6.0之前移除了httpclient
/**
* 开源框架httpclient向服务器提交数据GET
*/
private void requestNetWorkByHttpClientGet(String userName, String passWord) {
String path = "http://192.168.3.100:8080/web/Login?userName=+" + userName + "&passWord" + passWord;
//1 打开浏览器
HttpClient client = new DefaultHttpClient();
//2 输入网址
HttpGet url = new HttpGet(path);
//3 回车
HttpResponse response = client.execute(url);
//获取状态行
StatusLine statusLine = response.getStatusLine();
//获取状态码
int code = statusLine.getStatusCode();
if(code == 200) {
HttpEntity entity = response.getEntity();
InputStream is = entity.getContent();
String textByPost = StringUtils.parseStream2String(is);
} else {
}
}
/**
* 开源框架httpclient向服务器提交数据POST
*/
private void requestNetWorkByHttpClientPost(String userName, String passWord) {
String path = "http://192.168.3.100:8080/web/Login";
//1 打开浏览器
HttpClient client = new DefaultHttpClient();
//2 输入网址
HttpPost url = new HttpPost(path);
//发送post数据
List parameters = new ArrayList<>();
parameters.add(new BasicNameValuePair("userName", userName));
parameters.add(new BasicNameValuePair("passWord", passWord));
UrlEncodedFormEntity requestEntity = new UrlEncodedFormEntity(parameters, "utf-8");
url.setEntity(requestEntity);
//3 回车
HttpResponse response = client.execute(url);
//获取状态行
StatusLine statusLine = response.getStatusLine();
//获取状态码
int code = statusLine.getStatusCode();
if(code == 200) {
HttpEntity entity = response.getEntity();
InputStream is = entity.getContent();
String textByPost = StringUtils.parseStream2String(is);
} else {
}
}
29、开源框架Asynchttpclient向服务器提交数据
/**
* 开源框架Asynchttpclient向服务器提交数据GET
*/
private void requestNetWorkByAsyncHttpClient(String userName, String passWord) {
String path = "http://192.168.3.100:8080/web/Login?userName=+" + userName + "&passWord" + passWord;
AsyncHttpClient client = new AsyncHttpClient();
client.get(path, new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
String str = new String(responseBody);
}
@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
}
});
}
/**
* 开源框架Asynchttpclient向服务器提交数据POST
*/
private void requestNetWorkByAsyncHttpClientPost(String userName, String passWord) {
String path = "http://192.168.3.100:8080/web/Login";
AsyncHttpClient client = new AsyncHttpClient();
//设置post数据
RequestParams params = new RequestParams();
params.put("userName", userName);
params.put("passWord", passWord);
client.post(path, params, new AsyncHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
}
@Override
public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
}
});
}
30、上传任意文件
(注:) Header[]导包一直不成功,未解决
public void onSuccess(int i, org.apache.http.Header[] headers, byte[] bytes) {
}`
/**
* 1 创建对象
* 2 设置要上传的文件
* 3 调用post方法上传
*/
public void upload(String url) {
//1 创建对象
AsyncHttpClient client = new AsyncHttpClient();
//2 设置要上传的文件
RequestParams params = new RequestParams();
String filePath = "/mnt/sdcard/12.txt";
File file = new File(filePath);
try {
params.put("file", file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//3 调用post方法上传
client.post(url, params, new AsyncHttpResponseHandler() {
@Override
public void onProgress(int bytesWritten, int totalSize) {
super.onProgress(bytesWritten, totalSize);
pbProgressBar.setMax(totalSize);
pbProgressBar.setProgress(bytesWritten);
}
@Override
public void onStart() {
super.onStart();
}
@Override
public void onFinish() {
super.onFinish();
}
@Override
public void onSuccess(int i, org.apache.http.Header[] headers, byte[] bytes) {
}
@Override
public void onFailure(int i, org.apache.http.Header[] headers, byte[] bytes, Throwable throwable) {
}
});
}
31、多线程下载
private static int threadCount = 3;
private static String path = "http://192.168.25.76:8080/download/123.mp4";
/**
* 多线程下载:
* 1 获取服务器资源大小
* 2 开启多个线程下载服务器对应的一块资源
* 3 每个线程干完活,整个服务器资源下载完毕
*/
public void multiThreadDownLoad() {
try {
URL url = new URL(path);
//2 打开http
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
//3 设置请求参数(默认是get)
conn.setRequestMethod("GET");
conn.setConnectTimeout(3000);
conn.setReadTimeout(3000);
//4 获取状态码
int code = conn.getResponseCode();
// 1 获取服务器资源大小
int fileLength = 0;
if(code == 200) {
fileLength = conn.getContentLength();
//创建一个空文件
RandomAccessFile raf = new RandomAccessFile("D:\\DOWNLOAD.tmp", "rw");
raf.setLength(fileLength);
raf.close();
}
//2 开启多个线程下载服务器对应的一块资源
int blockSize = fileLength / threadCount;
for (int threadId = 0; threadId < threadCount; threadId++) {
int startIndex = threadId*blockSize;
int endIndex = (threadId+1)*blockSize-1;
//最后一个线程,修正结束位置
if(threadId == threadCount - 1) {
endIndex = fileLength - 1;
}
new DownLoadThread(startIndex, endIndex, threadId).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取文件名
* @return
*/
static String getFilePath() {
int index = path.lastIndexOf("/") + 1;
return "D:\\" + path.substring(index);
}
static class DownLoadThread extends Thread {
int startIndex;
int endIndex;
int threadId;
public DownLoadThread(int startIndex, int endIndex, int threadId) {
super();
this.startIndex = startIndex;
this.endIndex = endIndex;
this.threadId = threadId;
}
@Override
public void run() {
super.run();
//获取网络数据
URL url = null;
try {
url = new URL(path);
//2 打开http
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
//3 设置请求参数(默认是get)
conn.setRequestMethod("GET");
conn.setConnectTimeout(3000);
conn.setReadTimeout(3000);
//重要,设置要请求的数据范围
conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
//4 获取状态码
int code = conn.getResponseCode();
if(code == 206) {
//5 获取服务器返回的二进制输入流
InputStream is = conn.getInputStream();
//每个线程下载的文件
RandomAccessFile raf = new RandomAccessFile(getFilePath(), "wr");
int len = -1;
byte[] buffer = new byte[1024 * 8];
//重要:定位到每个线程开始写的位置
raf.seek(startIndex);
while ((len = is.read(buffer)) != -1) {
raf.write(buffer, 0, len);
}
raf.close();
}
//3 每个线程干完活,整个服务器资源下载完毕
} catch (Exception e) {
e.printStackTrace();
}
}
}
32、多线程下载原理

33、断点下载原理
断点时存储下载的位置,下一次下载前从上一次断点的位置开始下载
int lastDownPos += len;
34、多线程断点下载
private static int threadCount = 3;
private static String path = "http://192.168.25.76:8080/download/123.mp4";
/**
* 多线程下载:
* 1 获取服务器资源大小
* 2 开启多个线程下载服务器对应的一块资源
* 3 每个线程干完活,整个服务器资源下载完毕
*/
public void multiThreadDownLoad() {
try {
URL url = new URL(path);
//2 打开http
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
//3 设置请求参数(默认是get)
conn.setRequestMethod("GET");
conn.setConnectTimeout(3000);
conn.setReadTimeout(3000);
//4 获取状态码
int code = conn.getResponseCode();
// 1 获取服务器资源大小
int fileLength = 0;
if(code == 200) {
fileLength = conn.getContentLength();
//创建一个空文件
RandomAccessFile raf = new RandomAccessFile("D:\\DOWNLOAD.tmp", "rw");
raf.setLength(fileLength);
raf.close();
}
//2 开启多个线程下载服务器对应的一块资源
int blockSize = fileLength / threadCount;
for (int threadId = 0; threadId < threadCount; threadId++) {
int startIndex = threadId*blockSize;
int endIndex = (threadId+1)*blockSize-1;
//最后一个线程,修正结束位置
if(threadId == threadCount - 1) {
endIndex = fileLength - 1;
}
new DownLoadThread(startIndex, endIndex, threadId).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取文件名
* @return
*/
static String getFilePath() {
int index = path.lastIndexOf("/") + 1;
return "D:\\" + path.substring(index);
}
/**
* 获取断点进度的临时文件
* @return
*/
static String getTmpFilePath(int threadId) {
return getFilePath()+threadId+".txt";
}
static class DownLoadThread extends Thread {
int startIndex;
int endIndex;
int threadId;
//断点位置
int lastDownPos;
public DownLoadThread(int startIndex, int endIndex, int threadId) {
super();
this.startIndex = startIndex;
this.endIndex = endIndex;
this.threadId = threadId;
lastDownPos = startIndex;//初始化
}
@Override
public void run() {
super.run();
File tmpFile = new File(getTmpFilePath(threadId));
if(tmpFile != null && tmpFile.length() > 0) {
try {
FileInputStream fis = new FileInputStream(tmpFile);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
String str = br.readLine();
//下载起始位置
startIndex = Integer.valueOf(str);
lastDownPos = startIndex;
br.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//获取网络数据
URL url = null;
try {
url = new URL(path);
//2 打开http
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
//3 设置请求参数(默认是get)
conn.setRequestMethod("GET");
conn.setConnectTimeout(3000);
conn.setReadTimeout(3000);
//重要,设置要请求的数据范围
conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
//4 获取状态码
int code = conn.getResponseCode();
if(code == 206) {
//5 获取服务器返回的二进制输入流
InputStream is = conn.getInputStream();
//每个线程下载的文件
RandomAccessFile raf = new RandomAccessFile(getFilePath(), "wr");
int len = -1;
byte[] buffer = new byte[1024 * 8];
//重要:定位到每个线程开始写的位置
raf.seek(startIndex);
while ((len = is.read(buffer)) != -1) {
raf.write(buffer, 0, len);
//断点:存储断点的位置
lastDownPos += len;
RandomAccessFile rwd = new RandomAccessFile(getTmpFilePath(threadId), "rwd");
rwd.write(String.valueOf(lastDownPos).getBytes());
rwd.close();
}
raf.close();
}
//3 每个线程干完活,整个服务器资源下载完毕
boolean isDelete = tmpFile.delete();
} catch (Exception e) {
e.printStackTrace();
}
}
}
35、移植java项目到android
自定义进度条
ProgressBar pb = (ProgressBar) View.inflate(this, R.layout.progressbar, null);
LinearLayout linearLayout = new LinearLayout(this);
linearLayout.addView(pb);
linearLayout.removeAllViews();
36、开源框架进行多线程断点下载(xUtils)
HttpUtils 进行文件下载
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EditText etPath = (EditText) findViewById(R.id.et_path);
final TextView tvPb = (TextView) findViewById(R.id.tv_pb);
final ProgressBar pb = (ProgressBar) findViewById(R.id.pb);
Button btDownload = (Button) findViewById(R.id.bt_download);
final String url = etPath.getText().toString().trim();
btDownload.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
HttpUtils httpUtils = new HttpUtils();
httpUtils.download(url, "/mnt/sdcard/gui.exe", false, new RequestCallBack() {
@Override
public void onSuccess(ResponseInfo responseInfo) {
File file = responseInfo.result;
String path = file.getAbsolutePath();
}
@Override
public void onFailure(HttpException e, String s) {
}
@Override
public void onStart() {
super.onStart();
}
@Override
public void onLoading(long total, long current, boolean isUploading) {
super.onLoading(total, current, isUploading);
pb.setMax((int)total);
pb.setProgress((int) current);
int precent = (int) (current * 100 / total);
tvPb.setText(precent+"%");
}
});
}
});
}
}
37、意图设置action
public void callPhone(View view) {
//1 创建意图对象
Intent intent = new Intent();
//2 设置动作
intent.setAction(Intent.ACTION_CALL);
//3 设置数据
intent.setData(Uri.parse("tel://10086"));
//4 打开电话拨号界面
startActivity(intent);
}
添加拨打电话的权限
注:在6.0之前没有问题,但是在6.0之后如果没有进行权限处理会报错
12-12 21:37:10.340 6763-6763/com.gordon.jcday06 E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.gordon.jcday06, PID: 6763
java.lang.IllegalStateException: Could not execute method for android:onClick
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:293)
at android.view.View.performClick(View.java:5646)
at android.view.View$PerformClick.run(View.java:22459)
at android.os.Handler.handleCallback(Handler.java:761)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:156)
at android.app.ActivityThread.main(ActivityThread.java:6523)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:942)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288)
at android.view.View.performClick(View.java:5646)
at android.view.View$PerformClick.run(View.java:22459)
at android.os.Handler.handleCallback(Handler.java:761)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:156)
at android.app.ActivityThread.main(ActivityThread.java:6523)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:942)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832)
Caused by: java.lang.SecurityException: Permission Denial: starting Intent { act=android.intent.action.CALL dat=tel:xxxxxxxxxxxxx cmp=com.android.server.telecom/.components.UserCallActivity } from ProcessRecord{e27260f 6763:com.gordon.jcday06/u0a129} (pid=6763, uid=10129) requires android.permission.CALL_PHONE
at android.os.Parcel.readException(Parcel.java:1665)
at android.os.Parcel.readException(Parcel.java:1618)
at android.app.ActivityManagerProxy.startActivity(ActivityManagerNative.java:3097)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1539)
at android.app.Activity.startActivityForResult(Activity.java:4391)
at android.support.v4.app.BaseFragmentActivityApi16.startActivityForResult(BaseFragmentActivityApi16.java:54)
at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:67)
at android.app.Activity.startActivityForResult(Activity.java:4335)
at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:720)
at android.app.Activity.startActivity(Activity.java:4697)
at android.app.Activity.startActivity(Activity.java:4665)
at com.gordon.jcday06.MainActivity.callPhone(MainActivity.java:25)
at java.lang.reflect.Method.invoke(Native Method)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288)
at android.view.View.performClick(View.java:5646)
at android.view.View$PerformClick.run(View.java:22459)
at android.os.Handler.handleCallback(Handler.java:761)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:156)
at android.app.ActivityThread.main(ActivityThread.java:6523)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:942)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832)
解决方法:
package com.gordon.jcday06;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button callPhone = (Button) findViewById(R.id.call_phone);
callPhone.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CALL_PHONE}, 1);
} else {
callPhone();
}
}
});
}
public void callPhone() {
//1 创建意图对象
Intent intent = new Intent();
//2 设置动作
intent.setAction(Intent.ACTION_CALL);
//3 设置数据
intent.setData(Uri.parse("tel://10086"));
//4 打开电话拨号界面
startActivity(intent);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 1:
if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
callPhone();
} else {
Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show();
}
break;
default:
}
}
}
38、开启一个activity(隐式意图)
public void secondActivity(View view) {
Intent intent = new Intent();
intent.setAction("ni mei a");
startActivity(intent);
}
//注:必须定义category
如果清单文件设置了data,在代码中一定要设置data
public void secondActivity(View view) {
Intent intent = new Intent();
intent.setAction("ni mei a");
intent.setData(Uri.parse("suibian://"));
startActivity(intent);
}
注:data和type不能共存
intent.setData(Uri.parse("suibian://"));
intent.setType("text/nihao");
显式意图
public void secondActivity(View view) {
Intent intent = new Intent(this, ThirdActivity.class);
startActivity(intent);
}
39、显式与隐式区别
显式:
隐式:可以跳到其他应用界面
40、 Intent跳转时传递数据(不可传递object类型)
数据类型:
八大基本类型
Serializable
Parcelable
bundle
Intent
第1页传入数据
public void secondClick(View view) {
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("name", "goudan");
intent.putExtra("age", 16);
startActivity(intent);
}
第2页获取的数据
Intent intent = getIntent();
String name = intent.getStringExtra("name");
int age = intent.getIntExtra("age", 20);
Bundle数据:
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
Bundle bundle = new Bundle();
bundle.putString("name", "gordon");
bundle.putString("age", "20");
intent.putExtra("b", bundle);
startActivity(intent);
获取的数据
Intent intent = getIntent();
Bundle bundle = intent.getBundleExtra("b");
String name = bundle.getString("name");
String age = bundle.getString("age");
41、
URI : 统一资源标识符
URL :统一资源定位符 是URI的子集
42、activity销毁时传递数据
//1 销毁时传递数据
public void startActivityForResult(Intent intent) {
startActivityForResult(intent, 1);
}
//3 等待返回的数据
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
String phoneNum = data.getStringExtra("phoneNum");
etNum.setText(phoneNum);
}
ConstactActivity.java
//2 返回的数据
Intent intent = new Intent();
intent.putExtra("phoneNum", constactNumber[position]);
setResult(10, intent);
finish();
43、activity的生命周期
onCreate
onStart
onResume
onPause
onStop
onDestroy
开启:onCreate onStart onResume
关闭:onPause onStop onDestroy
最小化:onPause onStop
最大化:onRestart onStart onResume
44、透明界面
45、横竖屏切换的生命周期
onPasue onStop onDestroy
onCreate onStart onResume
固定屏幕朝向
android:screenOrientation="landscape" 横屏
android:screenOrientation="portrait" 竖屏
android:screenOrientation="sensor"
不敏感屏幕朝向
android:configChanges="orientation|keyboard|screenSize"
46、任务栈
1 标准模式
2 单一顶部模式:开启目标activity,系统会去任务栈的顶部查找,如果栈顶有复用,如果没有则创建新的
android:launchMode="singleTop"
应用:系统浏览器书签
3 单一任务模式
开启目标activity,系统会去整个任务栈查找,如果找到这个activity存在就清理这个activity上面的所有activity,如果没有就在栈顶创建一个实例对象
4 单一实例模式
开启目标activity,系统为这个activity单独创建任务栈
应用:系统来电界面
47、广播接收者
第1步:清单文件注册
第2步:
public class SDReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
String action = intent.getAction();
if("android.intent.action.MEDIA_MOUNTED".equals(action)) {
} else if("android.intent.action.MEDIA_UNMOUNTED".equals(action)) {
}
throw new UnsupportedOperationException("Not yet implemented");
}
}
48、服务
Intent intent = new Intent(this, BgService.class);
startService(intent);
49、线程和进程
50、服务的生命周期
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
服务可以被开启多次,每次开启执行onStartCommand方法
08-01 go
08-02 服务的生命周期之start
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
super.onCreate();
System.out.println("onCreate...");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("onStartCommand...");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("onDestroy...");
}
}
停止服务:
stopService(intent);
开启服务:
intent = new Intent(this, MyService.class);
startService(intent);
1、长期运行在后台
2、服务可以被多次开启,每次开启都调用onStartCommand
3、服务只能被停止一次,多次停止无效
08-03 服务的生命周期之bind
/**
* bindServiceIntent,
* conn, activity and service bind
* falg, BIND_AUTO_CREATE 连接时如果有服务对象就复用,没有创建新的服务对象
*/
private void bindService() {
Intent bindServiceIntent = new Intent(this, MyBindService.class);
bindService(bindServiceIntent, new MyConn(),BIND_AUTO_CREATE);
}
private class MyConn implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
08-04 绑定服务调方法
public class MyBindService extends Service {
public MyBindService() {
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("MyBindService:onStartCommand...");
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
System.out.println("绑定服务成功后调用onBind...");
return null;
}
@Override
public boolean onUnbind(Intent intent) {
System.out.println("服务解绑前调用onBind...");
return super.onUnbind(intent);
}
@Override
public void onCreate() {
super.onCreate();
System.out.println("MyBindService:onCreate...");
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("MyBindService:onDestroy...");
}
}
绑定服务:
onCreate
onBind
解绑服务:
onUnbind
onDestroy
不能长期运行在后台
只能被绑定一次,多次绑定无效
只能被解绑一次,多次解绑抛出异常
private void unbindService() {
unbindService(conn);
}
/**
* bindServiceIntent,
* conn, activity and service bind
* falg, BIND_AUTO_CREATE 连接时如果有服务对象就复用,没有创建新的服务对象
*/
private void bindService() {
bindServiceIntent = new Intent(this, MyBindService.class);
conn = new MyConn();
bindService(bindServiceIntent, conn,BIND_AUTO_CREATE);
}
private class MyConn implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
activity和service是同生共死,此时如果activity先销毁会抛出异常,一定要在onDestroy()中解绑服务
08-05 绑定服务调服务里的方法
1、绑定服务
2、在服务里创建内部类,调用服务里的方法
public void methodInService() {
System.out.println("我是服务里的方法");
}
public class ServiceConnClass extends Binder {
public void playMusic() {
methodInService();
}
}
3、在onBind() 返回
@Override
public IBinder onBind(Intent intent) {
return new ServiceConnClass();
}
4、activity的conn里的
private class MyConn implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyBindServiceCall.ServiceConnClass mService = (MyBindServiceCall.ServiceConnClass)service;
//调用,在其他需要调用的地方
mService.playMusic();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
通过服务里的内部类,调用服务里的方法
08-06 采用日志观察绑定服务调用方法的流程
08-07 绑定服务抽取接口
1、绑定service
Intent intent = new Intent(this, ThirdActivityConnService.class);
conn = new MyConn();
bindService(intent, conn,BIND_AUTO_CREATE);
2、定义内部类
public void zhuanqian() {
System.out.println("赚钱");
}
private class InnerService extends Binder implements IThirdActivityConnService {
@Override
public void qianshouMM() {
zhuanqian();
}
public void xiSangNa() {
System.out.println("洗桑拿");
}
}
3、定义接口
public interface IThirdActivityConnService {
void qianshouMM();
}
4、强转,调接口中的方法
private class MyConn implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = (IThirdActivityConnService)service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
08-08 混合方式开启服务
08-09 MediaPlayer 简单介绍
08-10 音乐播放器
08-11 start开启远程服务
远程服务
package cn.nubia.remoteserviceaidldemo;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class RemoteService extends Service {
public RemoteService() {
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
System.out.println("远程服务 onCreate...");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("远程服务 onStartCommand...");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("远程服务 onDestroy...");
}
}
注册清单文件:
本地服务
Button startService = (Button) findViewById(R.id.start_server_service);
startService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startServerService();
}
});
Button stopService = (Button) findViewById(R.id.stop_server_service);
stopService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
stopServerService();
}
});
private void stopServerService() {
stopService(intent);
}
private void startServerService() {
intent = new Intent();
intent.setAction("chu.lian");
startService(intent);
}
点击本地服务报错
2018-10-02 21:58:16.144 13783-13783/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: cn.nubia.localserviceaidldemo, PID: 13783
java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=chu.lian }
at android.app.ContextImpl.validateServiceIntent(ContextImpl.java:1644)
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1685)
at android.app.ContextImpl.startService(ContextImpl.java:1657)
at android.content.ContextWrapper.startService(ContextWrapper.java:644)
at cn.nubia.localserviceaidldemo.MainActivity.startServerService(MainActivity.java:40)
at cn.nubia.localserviceaidldemo.MainActivity.access$000(MainActivity.java:9)
at cn.nubia.localserviceaidldemo.MainActivity$1.onClick(MainActivity.java:21)
at android.view.View.performClick(View.java:6291)
at android.view.View$PerformClick.run(View.java:24931)
at android.os.Handler.handleCallback(Handler.java:808)
at android.os.Handler.dispatchMessage(Handler.java:101)
at android.os.Looper.loop(Looper.java:166)
at android.app.ActivityThread.main(ActivityThread.java:7425)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)
解决办法:必须指定具体的包名
private void startServerService() {
intent = new Intent();
intent.setAction("chu.lian");
intent.setPackage("cn.nubia.remoteserviceaidldemo");
startService(intent);
}
08-12 bind绑定远程服务
多次解绑服务抛出异常
2018-10-02 22:26:22.855 22936-22936/cn.nubia.localserviceaidldemo E/AndroidRuntime: FATAL EXCEPTION: main
Process: cn.nubia.localserviceaidldemo, PID: 22936
java.lang.IllegalArgumentException: connection is null
at android.app.ContextImpl.unbindService(ContextImpl.java:1815)
at android.content.ContextWrapper.unbindService(ContextWrapper.java:697)
at cn.nubia.localserviceaidldemo.MainActivity.unbindServerService(MainActivity.java:72)
at cn.nubia.localserviceaidldemo.MainActivity.access$300(MainActivity.java:12)
at cn.nubia.localserviceaidldemo.MainActivity$4.onClick(MainActivity.java:48)
at android.view.View.performClick(View.java:6291)
at android.view.View$PerformClick.run(View.java:24931)
at android.os.Handler.handleCallback(Handler.java:808)
at android.os.Handler.dispatchMessage(Handler.java:101)
at android.os.Looper.loop(Looper.java:166)
at android.app.ActivityThread.main(ActivityThread.java:7425)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)
package cn.nubia.localserviceaidldemo;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private Intent intent;
private MyConn conn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startService = (Button) findViewById(R.id.start_server_service);
startService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startServerService();
}
});
Button stopService = (Button) findViewById(R.id.stop_server_service);
stopService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
stopServerService();
}
});
Button bindService = (Button) findViewById(R.id.bind_server_service);
bindService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bindServerService();
}
});
Button unbindService = (Button) findViewById(R.id.unbind_server_service);
unbindService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
unbindServerService();
}
});
intent = new Intent();
intent.setAction("chu.lian");
intent.setPackage("cn.nubia.remoteserviceaidldemo");
}
private void stopServerService() {
stopService(intent);
}
private void startServerService() {
startService(intent);
}
private void bindServerService() {
conn = new MyConn();
bindService(intent, conn,BIND_AUTO_CREATE);
}
private void unbindServerService() {
unbindService(conn);
}
public class MyConn implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
}
08-13 调用远程组件里的方法
1、在远程服务中创建接口
package cn.nubia.remoteserviceaidldemo;
interface IRemoteService {
void eat();
void drink();
void wan();
void le();
}
2、远程服务中内部类实现接口
private class RemoteServiceInner extends Binder implements IRemoteService {
@Override
public void eat() {
eatone();
}
@Override
public void drink() {
drinkone();
}
@Override
public void wan() {
wanone();
}
@Override
public void le() {
leone();
}
}
3、在onBind 返回 内部类
@Override
public IBinder onBind(Intent intent) {
System.out.println("远程服务 onBind...");
Toast.makeText(this, "远程服务 onBind...", Toast.LENGTH_SHORT).show();
return new RemoteServiceInner();
}
4、在远程服务端创建aidl文件,将服务端的接口文件改名为IRemoteService.aidl
package cn.nubia.remoteserviceaidldemo;
interface IRemoteService {
void eat();
void drink();
void wan();
void le();
}
5、aidl 在本地获取远程服务对象
08-14 aidl编写步骤
android studio中aidl文件的位置:
https://blog.csdn.net/glen1943/article/details/51777031
1、在远程服务端将.java接口,改为.aidl
2、去掉权限修饰符
3、找到.aidl对应的.java文件,路径:
generated/source/aidl/debug/和java相同的包名
4、修改内部类继承Stub
private class RemoteServiceInner extends IRemoteService.Stub {
@Override
public void eat() {
eatone();
}
@Override
public void drink() {
drinkone();
}
@Override
public void wan() {
wanone();
}
@Override
public void le() {
leone();
}
}
5、在另一个apk调用远程服务的方法
创建和远程服务一样的包
将远程服务的.aidl文件复制到创建的包中
android studio 需要创建在main下创建与java同级的文件夹并且包名相同(客户端)
6、强转
public class MyConn implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iRemoteService = IRemoteService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
7、调方法
Button callDrink = (Button) findViewById(R.id.call_drink);
callDrink.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
iRemoteService.drink();
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
08-15 远程服务应用场景
1、android源码
2、手机制造厂商的服务
3、 超级大的公司写远程服务给其他程序员用
08-16 支付宝
1、创建一个服务AlipayService.java
2、在清单文件中添加隐示意图的action
3、创建内部类继承Binder实现接口
@Override
public IBinder onBind(Intent intent) {
return new AlipayServiceInner();
}
private class AlipayServiceInner extends Binder implements IAlipayService {
@Override
public int logoinAlipay(String userName, String passWord, String sfzh) {
if (userName.equals("gordon") && passWord.equals("123456") && sfzh.equals("610302xxxx")) {
return 1;
} else {
return 0;
}
}
}
4、另一个应用Wkings调用支付宝接口logoinAlipay()
开启远程服务:隐示意图开启服务
intent = new Intent();
intent.setAction("android.intent.action.PAY");
intent.setPackage("cn.nubia.alipay");
startService(intent);
5、在Alipay中,创建.aidl,在main下创建java的同级目录aidl
6、将内部类继承修改为
private class AlipayServiceInner extends IAlipayService.Stub {
@Override
public int logoinAlipay(String userName, String passWord, String sfzh) {
if (userName.equals("gordon") && passWord.equals("123456") && sfzh.equals("610302xxxx")) {
return 1;
} else {
return 0;
}
}
}
7、删除接口文件
远程服务端目录
客户端
1、绑定服务开启
private void startAlipay() {
startService(intent);
conn = new WkingsServiceConnection();
bindService(intent, conn, BIND_AUTO_CREATE);
}
public class WkingsServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
2、将Alipay的整个aidl包拷贝到Wkings main目录下
3、拿到远程接口对象
public class WkingsServiceConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iAlipayService = IAlipayService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
4、调方法
private void loginAlipay() {
try {
int state = iAlipayService.logoinAlipay("gordon", "123456", "610302xxxx");
if(state == 0) {
Toast.makeText(this, "账号不存在", Toast.LENGTH_SHORT).show();
} else if (state == 1) {
Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
09-02 内容提供者的概念
数据库的创建
java中创建普通文件:
1、在内存中创建一个指向d盘的文件对象
File file = new File("D:\suibian.txt");
2、在硬盘上创建文件并写入内容
FileOutputStream fos = new FileOutputStream(file);
fos.write("hehe".getBytes());
fos.close();
sqlite数据库:
1、在内存中创建数据库帮助类的对象
2、在磁盘上创建数据库文件
09-03 数据库的复习
package cn.nubia.contentproviderdemo;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class BankDbOpenHelper extends SQLiteOpenHelper {
/**
* @param context
*
*/
public BankDbOpenHelper(Context context) {
//super(context, name, factory, version);
super(context, "bank.db", null, 1);
}
/**
* 第一次创建数据库时调用,做初始化
* @param db
*/
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table account(_id integer primary key autoincrement, name varchar(20),money varchar(20))");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
2、调用
BankDbOpenHelper helper = new BankDbOpenHelper(this);
helper.getWritableDatabase();
创建数据库成功
路径:data/data/包名/databases/bank.db
-rw-rw--- 不可读不可写
09-05 内容提供者的编写步骤
1、创建一个类继承ContentProvider
package cn.nubia.contentproviderdemo;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
public class BankProvider extends ContentProvider {
public BankProvider() {
}
@Override
public boolean onCreate() {
// TODO: Implement this to initialize your content provider on startup.
return false;
}
@Override
public String getType(Uri uri) {
// TODO: Implement this to handle requests for the MIME type of the data
// at the given URI.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO: Implement this to handle requests to insert a new row.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// Implement this to handle requests to delete one or more rows.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO: Implement this to handle requests to update one or more rows.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO: Implement this to handle query requests from clients.
throw new UnsupportedOperationException("Not yet implemented");
}
}
2、在清单文件中配置authorities暗号上半句
3、暗号下半句在BankProvider中
//暗号下半句
static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
/**
* authority:暗号上半句,在清单文件中配置
* path:暗号下半句
* code:匹配码 如果匹配成功 1,不匹配:默认-1
*/
static {
matcher.addURI("tian.wang.gai.di.hu", "bao.ta.zhen.he.yao", 1);
}
4、实现增删改查
在onCreate中初始化
@Override
public boolean onCreate() {
mHelper = new BankDbOpenHelper(getContext());
db = mHelper.getWritableDatabase();
return false;
}
增:
@Override
public Uri insert(Uri uri, ContentValues values) {
int code = matcher.match(uri);
if (code == URI_SUCC) {
long id = db.insert("account", null, values);
return Uri.parse("id:" + id);
} else {
throw new IllegalArgumentException("无权操作");
}
}
user 调用另一个程序中的内容提供者
内容解析器调用内容提供者修改数据库
//1 获取内容解析器
ContentResolver resolver = getContentResolver();
//2 指定URI
Uri uri = Uri.parse("content://tian.wang.gai.di.hu/bao.ta.zhen.he.yao");
//3 操作数据库
ContentValues values = new ContentValues();
values.put("name","gordon");
values.put("money",1000000);
Uri id = resolver.insert(uri, values);
异常:如果只创建了user没有内容提供者
2018-10-03 16:20:46.900 3486-3486/cn.nubia.user E/AndroidRuntime: FATAL EXCEPTION: main
Process: cn.nubia.user, PID: 3486
java.lang.IllegalArgumentException: Unknown URI content://tian.wang.gai.di.hu/bao.ta.zhen.he.yao
at android.content.ContentResolver.insert(ContentResolver.java:1553)
at cn.nubia.user.MainActivity$1.onClick(MainActivity.java:31)
at android.view.View.performClick(View.java:6291)
at android.view.View$PerformClick.run(View.java:24931)
at android.os.Handler.handleCallback(Handler.java:808)
at android.os.Handler.dispatchMessage(Handler.java:101)
at android.os.Looper.loop(Looper.java:166)
at android.app.ActivityThread.main(ActivityThread.java:7425)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)
09-06 虚拟短信
new Thread(){
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://sms/");
ContentValues values = new ContentValues();
values.put("address","95533");
values.put("read",0);
values.put("type",1);
values.put("body","我是测试短信");
values.put("data", System.currentTimeMillis());
resolver.insert(uri,values);
}
}.start();
09-07 利用内容提供者查询数据库
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
int code = matcher.match(uri);
if (code == URI_SUCC_ACCOUNT) {
Cursor cursor = db.query("account", projection, selection, selectionArgs, null, null, sortOrder);
return cursor;
} else {
throw new IllegalArgumentException("无权查询");
}
}
09-08 利用内容提供者修改数据库
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
int code = matcher.match(uri);
if (code == URI_SUCC_ACCOUNT) {
int id = db.update("account", values, selection, selectionArgs);
return id;
} else {
return 0;
}
}
09-09 利用内容提供者删除数据库
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int code = matcher.match(uri);
if (code == URI_SUCC_ACCOUNT) {
int id = db.delete("account", selection, selectionArgs);
return id;
} else {
return 0;
}
}
user:
package cn.nubia.user;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button insertDb = (Button) findViewById(R.id.insert_db);
insertDb.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//1 获取内容解析器
ContentResolver resolver = getContentResolver();
//2 指定URI
Uri uri = Uri.parse("content://tian.wang.gai.di.hu/account");
//3 操作数据库
ContentValues values = new ContentValues();
values.put("name", "gordon");
values.put("money", 1000000);
Uri id = resolver.insert(uri, values);
Toast.makeText(MainActivity.this, uri.toString() + ",id:" + id, Toast.LENGTH_SHORT).show();
}
});
Button delteDb = (Button) findViewById(R.id.delte_db);
delteDb.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//1 获取内容解析器
ContentResolver resolver = getContentResolver();
//2 指定URI
Uri uri = Uri.parse("content://tian.wang.gai.di.hu/account");
//3 操作数据库
int id = resolver.delete(uri, "name=?", new String[]{"gordon"});
}
});
Button updateDb = (Button) findViewById(R.id.update_db);
updateDb.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//1 获取内容解析器
ContentResolver resolver = getContentResolver();
//2 指定URI
Uri uri = Uri.parse("content://tian.wang.gai.di.hu/account");
//3 操作数据库
ContentValues values = new ContentValues();
values.put("money", 1);
resolver.update(uri, values, "_id=?", new String[]{"1"});
}
});
Button queryDb = (Button) findViewById(R.id.query_db);
queryDb.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//1 获取内容解析器
ContentResolver resolver = getContentResolver();
//2 指定URI
Uri uri = Uri.parse("content://tian.wang.gai.di.hu/account");
//3 操作数据库
Cursor cursor = resolver.query(uri, new String[]{"name", "money"}, null, null, null);
while (cursor.moveToNext()) {
//String name = cursor.getString(0);
String name = cursor.getString(cursor.getColumnIndex("name"));
String money = cursor.getString(cursor.getColumnIndex("money"));
}
}
});
}
}
ContentProvider:
package cn.nubia.contentproviderdemo;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
public class BankProvider extends ContentProvider {
//暗号下半句
static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
private static final int URI_SUCC_ACCOUNT = 4;
/**
* authority:暗号上半句,在清单文件中配置
* path:暗号下半句
* code:匹配码 如果匹配成功 1,不匹配:默认-1
*/
static {
//推荐使用表名
matcher.addURI("tian.wang.gai.di.hu", "account", URI_SUCC_ACCOUNT);
}
private BankDbOpenHelper mHelper;
private SQLiteDatabase db;
public BankProvider() {
}
@Override
public boolean onCreate() {
mHelper = new BankDbOpenHelper(getContext());
db = mHelper.getWritableDatabase();
return false;
}
@Override
public String getType(Uri uri) {
// TODO: Implement this to handle requests for the MIME type of the data
// at the given URI.
throw new UnsupportedOperationException("Not yet implemented");
}
/**
* @param uri content://tian.wang.gai.di.hu/bao.ta.zhen.he.yao
* @param values
* @return
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
int code = matcher.match(uri);
if (code == URI_SUCC_ACCOUNT) {
long id = db.insert("account", null, values);
return Uri.parse("id:" + id);
} else {
throw new IllegalArgumentException("无权增加");
}
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int code = matcher.match(uri);
if (code == URI_SUCC_ACCOUNT) {
int id = db.delete("account", selection, selectionArgs);
return id;
} else {
return 0;
}
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
int code = matcher.match(uri);
if (code == URI_SUCC_ACCOUNT) {
int id = db.update("account", values, selection, selectionArgs);
return id;
} else {
return 0;
}
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
int code = matcher.match(uri);
if (code == URI_SUCC_ACCOUNT) {
Cursor cursor = db.query("account", projection, selection, selectionArgs, null, null, sortOrder);
return cursor;
} else {
throw new IllegalArgumentException("无权查询");
}
}
}
BankDbOpenHelper:
package cn.nubia.contentproviderdemo;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class BankDbOpenHelper extends SQLiteOpenHelper {
/**
* @param context
*
*/
public BankDbOpenHelper(Context context) {
//super(context, name, factory, version);
super(context, "bank.db", null, 1);
}
/**
* 第一次创建数据库时调用,做初始化
* @param db
*/
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table account(_id integer primary key autoincrement, name varchar(20),money varchar(20))");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
09-10 _getType方法
返回的数据类型
vnd.android.cursor.item 单行数据
vnd.android.cursor.dir/ 多行数据
09-11 学习内容提供者的目的
09-12 短信数据库的表结构&URI
09-13 利用内容提供者读取系统短信
09-14 备份短信
09-15 插入短信
09-16 利用内容提供者操作多张表
09-17 通讯录表结构&URI
09-18 读取通讯录联系人
09-19 插入通讯录联系人
09-20 内容观察者原理
09-21 内容观察者编写步骤
09-22 短信窃听器
项目一:手机安全卫士-MobileSecurityGuard
快捷键:
Ctrl+alt+f:生成全局变量
Ctrl+d:复制一行
1. Ctrl+P 查看参数
1. Ctrl+G
同时按下Ctrl+G快捷键弹出快速定位框,在框中输入行数点击OK即可快速切换到对应的行数,如图2.17所示。
2. Ctrl+E
同时按下Ctrl+E快捷键,弹出最近打开文件列表,可以快速选择最近曾经打开的文件
3. Ctrl+/
选中某一行,同时按下Ctrl+/快捷键可以注释这一行,如图2.19所示。
4. Ctrl+F
同时按下Ctrl+F快捷键,将在编辑页的顶部弹出类内快速搜索栏,可以快速定位类内的某个单词,支持联想查找
输入prote,将会高亮显示protected,同时注意到搜索栏中有三个复选框,选中第一个Match Case复选框将会对大小写敏感。
5. Ctrl+R:
Ctrl+F快捷键常和Ctrl+R快捷键使用,用来快速查找并全部替换
先使用快捷键Ctrl+F搜索出所有protected,然后使用快捷键Ctrl+R弹出替换栏,在替换栏输入框中输入替换后的单词并点击Replace all按钮即可将类中所有的protected替换成public,十分快捷。不过,在实际开发中要谨慎使用,避免引入不容易察觉的问题。
6. Ctrl+J
同时按下Ctrl和J快捷键,弹出快捷代码框
对于一些常用的代码Android Studio中进行了封装,直接选中即可快速生成,在开发中十分实用,这里以打印log和弹出Toast为例。首先按下Ctrl+J快捷键,弹出如图2.24所示的快捷代码框,然后直接输入logd这一快捷代码的“命令”,如图2.25所示。
打印Log需要TAG,在类的最上方输入快捷代码logt,即可快速生成一个TAG,如图2.27所示。
同样,先输入Ctrl+J键,弹出快捷代码框,然后直接输入toast,如图2.29所示。
按下Enter键,或者有了Toast以后按下Ta
快速生成了一行Toast语句,在引号中输入要Toast显示的信息即可,是不是十分快捷方便?
7. Ctrl+F12:
在类中方法比较多的情况下,同时按下Ctrl和F12键可以快速查看类中所有的方法,弹出这个框的同时可以直接输入想要搜索的方法,进行快速匹配。
1. Ctrl+Alt+T
选中一块代码,同时按下Ctrl、Alt和T键,弹出“包裹”弹出框,选择需要包裹的类型即可包裹选中的代码,
2. Ctrl+Alt+L
对当前类的所有代码进行格式化
2. Ctrl+Alt+V
此快捷键可以快速声明一个变量,本地变量赋值
3. Ctrl+Alt+H
点中某一个方法按下这个快捷键,在左边栏上弹出此方法的调用关系,此快捷键在开发中十分常用。
4. Ctrl+Alt+O
这个快捷键可以自动导包或删除无用的包,这时候按下快捷键即可自动删除这些无用的包
1. Ctrl+Shift+/
和Ctrl+/类似,都是实现注释代码的功能,Ctrl+Shift+/实现代码块的注释,再次按下这个快捷键将反注释掉这部分代码
这个快捷键在开发中经常使用,可以通过关键字快速搜索需要的信息,选中第一个复选框对大小写敏感。点击右边的标签即可查看关键字的预览
3. Ctrl+Shift+加号/减号
若方法是收起的,同时按下Ctrl+Shift+加号会将方法展开,
相反,若方法是展开的,同时按下Ctrl+Shift+减号则会收起方法
1.Alt+Insert
同时按下Alt和Insert键,弹出快速代码生成框,有构造方法、getter/setter方法、toString方法等
Android Studio快速代码生成框
这里以生成构造方法为例,选择Constructor选项
可以看出,自动生成了包含两个属性的构造方法,很是方便快捷,生成getter/setter方法和生成构造方法比较类似,同样选中这两个属性并按下快捷键,选中Getter and Setter,如图2.52所示。
选择OK键即生成这两个属性的getter和setter方法
2. Alt+鼠
按下Alt键并结合鼠标可以同时选中多
Android Studio多行选中
上图中一次选中了多行,此时可以进行多行编辑
3. Ctrl+鼠标左键
此快捷键可以查看鼠标选中的类或方法
MSG 01-06 闪屏界面开发
1 展示品牌,logo
2 初始化数据
3 合法性校验 检测是否有网络 检测是否已经登录
4 检测版本更新
-->隐藏ActionBar
MSG 01-07 修改应用名称和图标
android:icon="@mipmap/heima"
android:label="@string/app_name"
MSG 01-08 版本信息获取和展示
在app/build.gradle
android {
compileSdkVersion 26
defaultConfig {
applicationId "cn.nubia.mobilesecurityguard"
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
PackageManager packageManager = getPackageManager();
PackageInfo packageInfo = packageManager.getPackageInfo(getPackageName(), 0);
versionName = packageInfo.versionName;
MSG 01-09 渐变动画
private void startAlphaAnim(View view) {
AlphaAnimation alphaAnimation = new AlphaAnimation(0.5f, 1);
alphaAnimation.setDuration(3000);
view.startAnimation(alphaAnimation);
}
MSG 01-10 版本升级流程
MSG 01-11 版本升级服务器
{“versionName”:“nubia X20”, “versionCode”:910, “updateInfo”:“fix a big bug”}
部署tomcat服务器:
webapps/ROOT/update.json
MSG 01-12 使用okhttputils获取网络数据
MSG 01-13 JsonObject解析
MSG 01-14 判断版本更新、升级对话框
private void showUpdateDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("版本更新");
builder.setMessage(updateInfo);
builder.setPositiveButton("立即更新", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.setNegativeButton("忽略更新", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//延迟2秒,进入主界面
enterActivity();
}
});
builder.show();
}
MSG 01-15 跳到主界面
private void enterActivity() {
startActivity(new Intent(this, HomeActivity.class));
finish();
}
MSG 动画
rlRoot = (RelativeLayout) findViewById(R.id.rl_root);
startAlphaAnim(rlRoot);
private void startAlphaAnim(View view) {
AlphaAnimation alphaAnimation = new AlphaAnimation(0.5f, 1);
alphaAnimation.setDuration(3000);
view.startAnimation(alphaAnimation);
}
MSG 01-16 使用okhttp下载文件
6.0之后下载文件需要动态权限,如果没有赋予报错:
下载失败java.io.FileNotFoundException: /storage/emulated/0/MSGuard.apk (Permission denied)
private void isPermissioned () {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
//申请WRITE_EXTERNAL_STORAGE权限
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
0);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
doNext(requestCode,grantResults);
}
private void doNext(int requestCode, int[] grantResults) {
if (requestCode == 0) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission Granted
downloadApk();
} else {
// Permission Denied
mHandler.sendEmptyMessageDelayed(0, 2000);
}
}
}
private void downloadApk() {
System.out.println("正在下载新版本!");
System.out.println("downloadApk新版本路径在:" + Environment.getExternalStorageDirectory().getAbsolutePath());
OkHttpUtils.get().url(downloadApkUrl).build().execute(new FileCallBack(Environment.getExternalStorageDirectory().getAbsolutePath(), "MSGuard.apk") {
@Override
public void onError(Call call, Exception e, int id) {
System.out.println("下载失败" + e);
}
@Override
public void onResponse(File response, int id) {
System.out.println("下载完成!");
Toast.makeText(SplashActivity.this, "新版本下载成功!路径为:" + Environment.getExternalStorageDirectory().getAbsolutePath().toString(), Toast.LENGTH_SHORT).show();
}
});
}
MSG 01-17 apk打包
详见文件
MSG 01-18 跳到系统安装界面
//https://blog.csdn.net/itxiaolong3/article/details/72868199
private void installApk(File response) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(response), "application/vnd.android.package-archive");
startActivity(intent);
}
6.0后报错:
https://blog.csdn.net/qq_36961698/article/details/78436478
--------- beginning of crash
09-12 21:12:15.187 15753-15753/cn.nubia.mobilesecurityguard E/AndroidRuntime: FATAL EXCEPTION: main
Process: cn.nubia.mobilesecurityguard, PID: 15753
android.os.FileUriExposedException: file:///storage/emulated/0/MSGuard.apk exposed beyond app through Intent.getData()
at android.os.StrictMode.onFileUriExposed(StrictMode.java:1975)
at android.net.Uri.checkFileUriExposed(Uri.java:2363)
at android.content.Intent.prepareToLeaveProcess(Intent.java:9975)
at android.content.Intent.prepareToLeaveProcess(Intent.java:9929)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1622)
at android.app.Activity.startActivityForResult(Activity.java:4751)
at android.support.v4.app.BaseFragmentActivityApi16.startActivityForResult(BaseFragmentActivityApi16.java:54)
at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:67)
at android.app.Activity.startActivityForResult(Activity.java:4691)
at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:720)
at android.app.Activity.startActivity(Activity.java:5112)
at android.app.Activity.startActivity(Activity.java:5080)
at cn.nubia.mobilesecurityguard.SplashActivity.installApk(SplashActivity.java:181)
at cn.nubia.mobilesecurityguard.SplashActivity.access$700(SplashActivity.java:34)
at cn.nubia.mobilesecurityguard.SplashActivity$5.onResponse(SplashActivity.java:172)
at cn.nubia.mobilesecurityguard.SplashActivity$5.onResponse(SplashActivity.java:161)
at com.zhy.http.okhttp.OkHttpUtils$3.run(OkHttpUtils.java:186)
at android.os.Handler.handleCallback(Handler.java:808)
at android.os.Handler.dispatchMessage(Handler.java:101)
at android.os.Looper.loop(Looper.java:166)
at android.app.ActivityThread.main(ActivityThread.java:7425)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)
修改:
https://blog.csdn.net/qq_36961698/article/details/78436478
报错:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:instantRunMainApkResourcesDebug'.
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:100)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:70)
at org.gradle.api.internal.tasks.execution.OutputDirectoryCreatingTaskExecuter.execute(OutputDirectoryCreatingTaskExecuter.java:51)
at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:62)
at org.gradle.api.internal.tasks.execution.ResolveTaskOutputCachingStateExecuter.execute(ResolveTaskOutputCachingStateExecuter.java:54)
at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:60)
at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:97)
at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:87)
at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:52)
at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54)
at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34)
at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.run(DefaultTaskGraphExecuter.java:248)
at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336)
at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328)
at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:199)
at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:110)
at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:241)
at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:230)
at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.processTask(DefaultTaskPlanExecutor.java:123)
at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.access$200(DefaultTaskPlanExecutor.java:79)
at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:104)
at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:98)
at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.execute(DefaultTaskExecutionPlan.java:626)
at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.executeWithTask(DefaultTaskExecutionPlan.java:581)
at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.run(DefaultTaskPlanExecutor.java:98)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.gradle.tooling.BuildException: Exception while generating InstantRun main resources APK
at com.android.build.gradle.internal.scope.BuildElements$ExecutorBasedScheduler$transform$2.invoke(BuildElements.kt:133)
at com.android.build.gradle.internal.scope.BuildElements$ExecutorBasedScheduler$transform$2.invoke(BuildElements.kt:110)
at kotlin.sequences.SequencesKt___SequencesKt$onEach$1.invoke(_Sequences.kt:1260)
at kotlin.sequences.TransformingSequence$iterator$1.next(Sequences.kt:148)
at kotlin.sequences.FilteringSequence$iterator$1.calcNext(Sequences.kt:108)
at kotlin.sequences.FilteringSequence$iterator$1.hasNext(Sequences.kt:132)
at kotlin.sequences.TransformingSequence$iterator$1.hasNext(Sequences.kt:152)
at kotlin.sequences.SequencesKt___SequencesKt.toCollection(_Sequences.kt:633)
at kotlin.sequences.SequencesKt___SequencesKt.toMutableList(_Sequences.kt:663)
at kotlin.sequences.SequencesKt___SequencesKt.toList(_Sequences.kt:654)
at com.android.build.gradle.internal.scope.BuildElements$ExecutorBasedScheduler.transform(BuildElements.kt:140)
at com.android.build.gradle.internal.scope.BuildElements$ExecutorBasedScheduler.into(BuildElements.kt:115)
at com.android.build.gradle.internal.scope.BuildElementActionScheduler.into(BuildElementActionScheduler.kt:32)
at com.android.build.gradle.tasks.ir.InstantRunMainApkResourcesBuilder.doFullTaskAction(InstantRunMainApkResourcesBuilder.kt:77)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:46)
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:39)
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:26)
at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:780)
at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:747)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$1.run(ExecuteActionsTaskExecuter.java:121)
at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336)
at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328)
at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:199)
at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:110)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:110)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:92)
... 32 more
Caused by: java.io.IOException: Exception while generating InstantRun main resources APK
at com.android.build.gradle.tasks.ir.InstantRunMainApkResourcesBuilder.processSplit(InstantRunMainApkResourcesBuilder.kt:106)
at com.android.build.gradle.tasks.ir.InstantRunMainApkResourcesBuilder$doFullTaskAction$1.invoke(InstantRunMainApkResourcesBuilder.kt:76)
at com.android.build.gradle.tasks.ir.InstantRunMainApkResourcesBuilder$doFullTaskAction$1.invoke(InstantRunMainApkResourcesBuilder.kt:51)
at com.android.build.gradle.internal.scope.BuildElements$ExecutorBasedScheduler$transform$$inlined$forEach$lambda$1.call(BuildElements.kt:121)
at com.android.build.gradle.internal.scope.BuildElements$ExecutorBasedScheduler$transform$$inlined$forEach$lambda$1.call(BuildElements.kt:110)
at java.util.concurrent.ForkJoinTask$AdaptedCallable.exec(ForkJoinTask.java:1424)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinTask.externalAwaitDone(ForkJoinTask.java:326)
at java.util.concurrent.ForkJoinTask.doJoin(ForkJoinTask.java:391)
at java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:719)
at com.android.ide.common.internal.WaitableExecutor.waitForAllTasks(WaitableExecutor.java:215)
at com.android.build.gradle.internal.scope.BuildElements$ExecutorBasedScheduler.transform(BuildElements.kt:125)
... 52 more
Caused by: com.android.ide.common.process.ProcessException: Failed to execute aapt
at com.android.builder.core.AndroidBuilder.processResources(AndroidBuilder.java:809)
at com.android.builder.core.AndroidBuilder.processResources(AndroidBuilder.java:797)
at com.android.build.gradle.internal.transforms.InstantRunSplitApkBuilder.generateSplitApkResourcesAp(InstantRunSplitApkBuilder.java:375)
at com.android.build.gradle.tasks.ir.InstantRunMainApkResourcesBuilder.processSplit(InstantRunMainApkResourcesBuilder.kt:91)
... 63 more
Suppressed: java.lang.RuntimeException: Some file processing failed, see logs for details
at com.android.builder.internal.aapt.QueuedResourceProcessor.waitForAll(QueuedResourceProcessor.java:121)
at com.android.builder.internal.aapt.QueuedResourceProcessor.end(QueuedResourceProcessor.java:141)
at com.android.builder.internal.aapt.v2.QueueableAapt2.close(QueueableAapt2.java:104)
at kotlin.io.CloseableKt.closeFinally(Closeable.kt:63)
at com.android.build.gradle.tasks.ir.InstantRunMainApkResourcesBuilder.processSplit(InstantRunMainApkResourcesBuilder.kt:88)
... 63 more
Caused by: java.util.concurrent.ExecutionException: java.util.concurrent.ExecutionException: com.android.builder.internal.aapt.v2.Aapt2Exception: AAPT2 error: check logs for details
at com.google.common.util.concurrent.AbstractFuture.getDoneValue(AbstractFuture.java:503)
at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture.java:482)
at com.google.common.util.concurrent.AbstractFuture$TrustedFuture.get(AbstractFuture.java:79)
at com.android.builder.internal.aapt.AbstractAapt.link(AbstractAapt.java:34)
at com.android.builder.core.AndroidBuilder.processResources(AndroidBuilder.java:807)
... 66 more
Caused by: java.util.concurrent.ExecutionException: com.android.builder.internal.aapt.v2.Aapt2Exception: AAPT2 error: check logs for details
at com.google.common.util.concurrent.AbstractFuture.getDoneValue(AbstractFuture.java:503)
at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture.java:462)
at com.google.common.util.concurrent.AbstractFuture$TrustedFuture.get(AbstractFuture.java:79)
at com.android.builder.internal.aapt.v2.QueueableAapt2.lambda$makeValidatedPackage$1(QueueableAapt2.java:166)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
... 1 more
Caused by: com.android.builder.internal.aapt.v2.Aapt2Exception: AAPT2 error: check logs for details
at com.android.builder.png.AaptProcess$NotifierProcessOutput.handleOutput(AaptProcess.java:443)
at com.android.builder.png.AaptProcess$NotifierProcessOutput.err(AaptProcess.java:395)
at com.android.builder.png.AaptProcess$ProcessOutputFacade.err(AaptProcess.java:312)
at com.android.utils.GrabProcessOutput$1.run(GrabProcessOutput.java:104)
AndroidManifest.xml中将provider放在application标签外了
错误:
Process: cn.nubia.mobilesecurityguard, PID: 27974
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.XmlResourceParser android.content.pm.ProviderInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' on a null object reference
at android.support.v4.content.FileProvider.parsePathStrategy(FileProvider.java:584)
at android.support.v4.content.FileProvider.getPathStrategy(FileProvider.java:558)
at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:400)
at cn.nubia.mobilesecurityguard.SplashActivity.installApkFile(SplashActivity.java:194)
at cn.nubia.mobilesecurityguard.SplashActivity.access$700(SplashActivity.java:37)
at cn.nubia.mobilesecurityguard.SplashActivity$5.onResponse(SplashActivity.java:175)
at cn.nubia.mobilesecurityguard.SplashActivity$5.onResponse(SplashActivity.java:164)
at com.zhy.http.okhttp.OkHttpUtils$3.run(OkHttpUtils.java:186)
at android.os.Handler.handleCallback(Handler.java:808)
at android.os.Handler.dispatchMessage(Handler.java:101)
at android.os.Looper.loop(Looper.java:166)
at android.app.ActivityThread.main(ActivityThread.java:7425)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)
09-12 21:44:47.478 27974-27974/cn.nubia.mobilesecurityguard I/Process: Sending signal. PID: 27974 SIG: 9
https://blog.csdn.net/qq_31588719/article/details/77224049
报错2:
--------- beginning of crash
09-16 12:15:19.304 4554-4554/cn.nubia.mobilesecurityguard E/AndroidRuntime: FATAL EXCEPTION: main
Process: cn.nubia.mobilesecurityguard, PID: 4554
java.lang.StringIndexOutOfBoundsException: length=19; index=20
at java.lang.String.substring(String.java:1939)
at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:721)
at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:401)
at cn.nubia.mobilesecurityguard.SplashActivity.installApkFile1(SplashActivity.java:207)
at cn.nubia.mobilesecurityguard.SplashActivity.access$700(SplashActivity.java:37)
at cn.nubia.mobilesecurityguard.SplashActivity$5.onResponse(SplashActivity.java:176)
at cn.nubia.mobilesecurityguard.SplashActivity$5.onResponse(SplashActivity.java:164)
at com.zhy.http.okhttp.OkHttpUtils$3.run(OkHttpUtils.java:186)
at android.os.Handler.handleCallback(Handler.java:808)
at android.os.Handler.dispatchMessage(Handler.java:101)
at android.os.Looper.loop(Looper.java:166)
at android.app.ActivityThread.main(ActivityThread.java:7425)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)
09-16 12:15:19.319 4554-4554/cn.nubia.mobilesecurityguard I/Process: Sending signal. PID: 4554 SIG: 9
未解决
MSG 01-19 下载进度更新
private void downloadApk() {
System.out.println("正在下载新版本!");
//展示下载进度
showDownloadProgress();
System.out.println("downloadApk新版本路径在:" + Environment.getExternalStorageDirectory().getAbsolutePath());
OkHttpUtils.get().url(downloadApkUrl).build().execute(new FileCallBack(Environment.getExternalStorageDirectory().getAbsolutePath(), "MSGuard.apk") {
@Override
public void onError(Call call, Exception e, int id) {
System.out.println("下载失败" + e);
}
@Override
public void onResponse(File response, int id) {
System.out.println("下载完成!");
Toast.makeText(SplashActivity.this, "新版本下载成功!路径为:" + Environment.getExternalStorageDirectory().getAbsolutePath().toString(), Toast.LENGTH_SHORT).show();
//跳到系统安装界面
String path = Environment.getExternalStorageDirectory().getAbsolutePath();
//installApkFile2(SplashActivity.this,path);
}
@Override
public void inProgress(float progress, long total, int id) {
super.inProgress(progress, total, id);
//progress:
//total:
int precent = (int) ((progress*100)/total);
progressDialog.setProgress(precent);
}
});
}
private void showDownloadProgress() {
progressDialog = new ProgressDialog(this);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setTitle("正在下载,请稍候...");
progressDialog.show();
}
下载完成
progressDialog..dimiss()
MSG 01-20 签名冲突
包名相同且签名相同才属于同一个应用
测试时需要打同样签名的包
MSG 01-21 细节处理 取消弹窗、取消安装
如果弹窗被取消,用户点击返回键或弹窗外侧
//监听弹窗被取消的事件(点击返回键或弹窗外侧)
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
enterActivity();
}
});
立即更新,取消安装时
private void installApkFile(Context context, String filePath) {
Intent intent = new Intent(Intent.ACTION_VIEW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(context, "cn.nubia.mobilesecurityguard.fileprovider", new File(filePath));
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
} else {
intent.setDataAndType(Uri.fromFile(new File(filePath)), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
**startActivityForResult(intent, 0);**
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
System.out.println("requestCode = " + requestCode);
System.out.println("用户取消了安装!");
finish();
}
MSG 02-02 主界面头布局
MSG 02-03 启动logo旋转动画
/**
* 动画: 属性、补间
* 属性动画,绕Y旋转
*/
private void startLogoAnimal() {
//ivLogo.setRotationY(180);
/**
* 1 执行动画的对象
* 2 要修改的属性
* 3 可变参数,值得变化范围
*/
ObjectAnimator animator = ObjectAnimator.ofFloat(ivLogo, "rotationY", 0, 360);
animator.setDuration(2000);
//无限循环
animator.setRepeatCount(ObjectAnimator.INFINITE);
//正向执行一次后逆向执行一次
animator.setRepeatMode(ObjectAnimator.REVERSE);
animator.start();
}
MSG 02-04 跑马灯效果
android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
android:singleLine="true"
MSG 02-05 自定义控件的三个构造方法
//开发者,通过代码创建一个对象时会走
public FocusedTextView(Context context) {
this(context, null);
System.out.println("构造方法1");
}
//当控件有属性时,由系统底层调用
public FocusedTextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, -1);
System.out.println("构造方法2");
}
//当控件配有样式时
public FocusedTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
System.out.println("构造方法3");
initView();
}
MSG 02-06 自定义有焦点的textview
1、写一个自定义控件继承TextView
/**
* android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
android:singleLine="true"
*/
private void initView() {
setEllipsize(TextUtils.TruncateAt.MARQUEE);
setFocusable(true);
setFocusableInTouchMode(true);
setSingleLine();
}
//当前的TextView是否有焦点
@Override
public boolean isFocused() {
//return super.isFocused();
return true;
}
2、xml中替换TextView
MSG 02-07 首页GridView填充数据
private String[] titles = new String[] { "手机防盗", "通讯卫士", "软件管理", "进程管理",
"流量统计", "手机杀毒", "缓存清理", "高级工具" };
private Integer[] icons = new Integer[] { R.drawable.sjfd, R.drawable.srlj,
R.drawable.rjgj, R.drawable.jcgl, R.drawable.lltj, R.drawable.sjsd,
R.drawable.hcql, R.drawable.cygj };
private String[] descs = new String[] { "远程定位手机", "全面拦截骚扰", "管理您的软件",
"管理运行进程", "流量一目了然", "病毒无处藏身", "系统快如火箭", "工具大全" };
1、将包含数据封装对象在集合中
private ArrayList mList;
2、创建对象
public class HomeInfo {
public String title;
public String des;
public int imageId;
}
3、创建适配器
class HomeAdapter extends BaseAdapter {
@Override
public int getCount() {
return mList.size();
}
//返回条目对象
@Override
public HomeInfo getItem(int position) {
return mList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = View.inflate(HomeActivity.this, R.layout.item_home, null);
ImageView ivIcon = (ImageView) view.findViewById(R.id.iv_image);
TextView tvTitle = (TextView) view.findViewById(R.id.tv_title);
TextView tvDes = (TextView) view.findViewById(R.id.tv_des);
HomeInfo info = mList.get(position);
ivIcon.setImageResource(info.imageId);
tvTitle.setText(info.title);
tvDes.setText(info.des);
return view;
}
}
4、初始化数据
private void initData() {
mList = new ArrayList<>();
for (int i = 0; i < titles.length; i++) {
HomeInfo homeInfo = new HomeInfo();
homeInfo.title = titles[i];
homeInfo.des = descs[i];
homeInfo.imageId = icons[i];
mList.add(homeInfo);
}
gvHome.setAdapter(new HomeAdapter());
}
MSG 02-08 状态选择器
1、在drawable下创建状态选择器home_item_selector.xml
按下去的状态
2、settings按钮监听点击事件
1、
2、
ivSettings = (ImageView) findViewById(R.id.iv_settings);
ivSettings.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
System.out.println("点击了设置按钮...");
}
});
MSG 02-09 9patch图的使用
android studio 修改 9 patch图
MSG 02-10 设置中心页面开发
Intent intent = new Intent(getApplicationContext(), SettingActivity.class);
startActivity(intent);
xml
MSG 02-11 抽取标题栏样式
1、在styles.xml中,抽取样式
2、用法
MSG 02-13 自定义组合控件—填充布局对象
1、将如上的relativeLayout布局单独抽取xml
2、由于上面的跟布局是RelativeLayout,定义类继承RelativeLayout
public class SettingItemView extends RelativeLayout {
public SettingItemView(Context context) {
this(context, null);
}
public SettingItemView(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public SettingItemView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
两种方法实现添加布局
private void initView() {
//给当前空布局添加布局对象 方法1
//参三:null加载的控件没有父控件
View view = View.inflate(getContext(), R.layout.setting_item_view, null);
addView(view);
//给当前空布局添加布局对象 方法2
//2 参三,以本类为父类SettingItemView this 在加载布局时,以当前类为父类
View view2 = View.inflate(getContext(), R.layout.setting_item_view, this);
}
}
MSG 02-14 自定义组合控件-修改标题和背景
View view = View.inflate(getContext(), R.layout.setting_item_view, null);
tvTitle = view.findViewById(R.id.tv_title);
ivToggle = view.findViewById(R.id.iv_toggle);
设置title
//公开一个方法,修改标题
public void setTitle(String title) {
tvTitle.setText(title);
}
在需要用到的地方
autoUpdateSetting = (SettingItemView) findViewById(R.id.auto_update_setting);
harassmentInterceptionSetting = (SettingItemView) findViewById(R.id.Harassment_interception_setting);
设置值
autoUpdateSetting.setTitle("自动更新设置");
autoUpdateSetting.setBackgroundResource(R.drawable.first_selector);
harassmentInterceptionSetting.setTitle("骚扰拦截设置");
autoUpdateSetting.setBackgroundResource(R.drawable.last_selector);
MSG 02-15 自定义组合控件-开关控制
1、在自定义组合控件类中定义一个boolean
//标记当前开关状态
private boolean isOpen;
//当前开关的状态
public boolean isToggleOn() {
return isOpen;
}
//修改当前开关状态,开关图片
public void setToggleOn(boolean isOpen) {
this.isOpen = isOpen;
if(isOpen) {
ivToggle.setImageResource(R.drawable.on);
} else {
ivToggle.setImageResource(R.drawable.off);
}
}
//开关 方法1
public void toggle() {
if(isOpen) {
setToggleOn(false);
} else {
setToggleOn(true);
}
}
//开关 方法2
public void toggle() {
//setToggleOn(!isOpen);
}
点击事件
1、
public class SettingActivity extends AppCompatActivity implements View.OnClickListener
2、
autoUpdateSetting = (SettingItemView) findViewById(R.id.auto_update_setting);
harassmentInterceptionSetting = (SettingItemView) findViewById(R.id.harassment_interception_setting);
autoUpdateSetting.setOnClickListener(this);
harassmentInterceptionSetting.setOnClickListener(this);
3、
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.auto_update_setting:
System.out.println("点击自动更新设置");
autoUpdateSetting.toggle();
break;
case R.id.harassment_interception_setting:
System.out.println("点击骚扰拦截设置");
harassmentInterceptionSetting.toggle();
break;
default:
break;
}
}
MSG 02-16 自动更新开关控制
更新开关状态存储
//初始化sp
config = getSharedPreferences("config", MODE_PRIVATE);
config.edit().putBoolean("auto_update_setting",autoUpdateSetting.isToggleOn()).commit();
根据拿到的值
//根据sp的值,更新开关状态
boolean autoUpdateSettingBeforeState = config.getBoolean("auto_update_setting", true);
boolean harassmentInterceptionSettingBeforeState = config.getBoolean("harassment_interception_setting", true);
autoUpdateSetting.setToggleOn(autoUpdateSettingBeforeState);
harassmentInterceptionSetting.setToggleOn(harassmentInterceptionSettingBeforeState);
在闪屏界面判断是否开启了版本更新
SharedPreferences config = getSharedPreferences("config", MODE_PRIVATE);
boolean isAutoUpdateSetting = config.getBoolean("auto_update_setting", true);
if (isAutoUpdateSetting) {
checkVersion();
}
MSG 02-17 sp工具类封装
package cn.nubia.mobilesecurityguard.gordon_utils;
import android.content.Context;
import android.content.SharedPreferences;
/**
* SharedPreferences 封装类
*/
public class PrefUtils {
/**
*
* @param context
* @param fileName 存储的文件名
* @return
*/
private static SharedPreferences getSharedPreferencesObject(Context context, String fileName) {
SharedPreferences sp = context.getSharedPreferences(fileName, Context.MODE_PRIVATE);
return sp;
}
/**
* 真值
* @param context
* @param fileName 存储的文件名
* @param key
* @param value
*/
public static void putBoolean(Context context, String fileName, String key, boolean value) {
getSharedPreferencesObject(context, fileName).edit().putBoolean(key,value).commit();
}
/**
*
* @param context
* @param fileName 存储的文件名
* @param key
* @param defValue
*/
public static boolean getBoolean(Context context, String fileName, String key, boolean defValue) {
boolean value = getSharedPreferencesObject(context, fileName).getBoolean(key, defValue);
return value;
}
/**
* 整数
* @param context
* @param fileName 存储的文件名
* @param key
* @param value
*/
public static void putInt(Context context, String fileName, String key, int value) {
getSharedPreferencesObject(context, fileName).edit().putInt(key,value).commit();
}
/**
*
* @param context
* @param fileName 存储的文件名
* @param key
* @param defValue
*/
public static int getInt(Context context, String fileName, String key, int defValue) {
int value = getSharedPreferencesObject(context, fileName).getInt(key, defValue);
return value;
}
/**
* 字符串
* @param context
* @param fileName 存储的文件名
* @param key
* @param value
*/
public static void putString(Context context, String fileName, String key, String value) {
getSharedPreferencesObject(context, fileName).edit().putString(key,value).commit();
}
/**
*
* @param context
* @param fileName 存储的文件名
* @param key
* @param defValue
*/
public static String getString(Context context, String fileName, String key, String defValue) {
String value = getSharedPreferencesObject(context, fileName).getString(key, defValue);
return value;
}
}
MSG 02-18 自定义组合控件小结
MSG 02-19 自定义属性-attrs文件声明
1、在values目录下创建attrs.xml
2、自定义命名空间
xmlns:nubia="http://schemas.android.com/apk/res-auto"
3、布局中配置
MSG 02-20 在布局文件中配置自定义属性
MSG 02-21 获取自定义属性并更新控件
public SettingItemView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
//1 从AttributeSet中取出自定义属性
//2 根据自定义属性的值更新相关控件
String nubia_title = attrs.getAttributeValue(NAMESPACE, "nubia_title");
boolean nubia_show_toggle = attrs.getAttributeBooleanValue(NAMESPACE, "nubia_show_toggle", false);
int nubia_background = attrs.getAttributeIntValue(NAMESPACE, "nubia_background", 0);
setTitle(nubia_title);
switch (nubia_background) {
case 0:
setBackgroundResource(R.drawable.first_selector);
break;
case 1:
setBackgroundResource(R.drawable.middle_selector);
break;
case 2:
setBackgroundResource(R.drawable.last_selector);
break;
}
}
MSG 02-22 自定义属性小结
MSG 02-23 总结
**MSG 03-02 自定义弹窗布局 **
自定义dialog布局
dialog_set_pwd.xml
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void showSetDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setView(R.layout.dialog_set_pwd);
builder.show();
}
MSG 03-03 弹窗布局控件获取并设置点击事件
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void showSetDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
View view = View.inflate(this, R.layout.dialog_set_pwd, null);
builder.setView(view);
btDetermine = view.findViewById(R.id.bt_determine);
btCancel = view.findViewById(R.id.bt_cancel);
etInputPwd = view.findViewById(R.id.et_input_pwd);
etPwdConfirm = view.findViewById(R.id.et_pwd_confirm);
//根据build设置的数据,构造一个dialog
final AlertDialog dialog = builder.create();
btDetermine.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dialog.dismiss();
}
});
btCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dialog.dismiss();
}
});
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialogInterface) {
}
});
//builder.show();
dialog.show();
}
MSG 03-04 保存密码&输入密码弹窗
点击确认时,保存密码,SharedPreferences存储
String pwd = etInputPwd.getText().toString().trim();
String pwdConfirm = etPwdConfirm.getText().toString().trim();
if (TextUtils.isEmpty(pwd) || TextUtils.isEmpty(pwdConfirm)) {
Toast.makeText(HomeActivity.this, "输入内容不能为空!", Toast.LENGTH_SHORT).show();
} else {
if (pwd.equals(pwdConfirm)) {
//保存密码
PrefUtils.putString(getApplicationContext(), GlobalConstants.SP_FILE, GlobalConstants.PREF_PASSWORD, pwd);
//PrefUtils.putString(getApplicationContext(),GlobalConstants.SP_FILE,GlobalConstants.PREF_PASSWORD_CONFIRM,pwdConfirm);
dialog.dismiss();
} else {
Toast.makeText(HomeActivity.this, "输入密码不一致!", Toast.LENGTH_SHORT).show();
}
}
**MSG 03-05 MD5介绍 **
特点:
1、可以将任何数据(字符串或文件)加密成32位长度的16进制字符串(0-f)
2、不可逆,只能加密,无法解密,单向加密算法
3、如果数据一样,那么计算出的md5就一定一样,如果不同,计算出的基本不同---哈希碰撞
MSG 03-06 设置向导1页面布局
方法1:
方法2:
**MSG 06-12 dialog显示在屏幕下方 **
//修改dialog所在窗口window的位置
Window window = getWindow();
WindowManager.LayoutParams params = window.getAttributes();
params.gravity = Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL;
window.setAttributes(params);
**MSG 06-13 给弹窗ListView设置数据 **
1、
package cn.nubia.mobilesecurityguard.view;
import android.app.Dialog;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.ListView;
import cn.nubia.mobilesecurityguard.R;
/**
* 自定义dialog
* 1、给dialog设置布局
* 2、需要清除标题栏
* 3、
*/
public class AddressDialog extends Dialog {
private final ListView lvAddress;
public AddressDialog(Context context) {
super(context,R.style.AddressDialogStyle);
setContentView(R.layout.address_dialog);
//修改dialog所在窗口window的位置
Window window = getWindow();
WindowManager.LayoutParams params = window.getAttributes();
params.gravity = Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL;
window.setAttributes(params);
lvAddress = findViewById(R.id.lv_address);
}
public void setAdapter(BaseAdapter adapter) {
lvAddress.setAdapter(adapter);
}
public void setOnItemClickListener(AdapterView.OnItemClickListener listener) {
lvAddress.setOnItemClickListener(listener);
}
}
2、
private void showAddressStyleDialog() {
//展示弹窗
AddressDialog dialog = new AddressDialog(SettingActivity.this);
//给dialog设置布局
dialog.setAdapter(new AddressStyleAdapter());
dialog.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
//1、记录当前被选中的条目的位置
PrefUtils.putInt(getApplicationContext(),GlobalConstants.SP_FILE, GlobalConstants.PREF_ADDRESS_STYLE, position);
//2、取消弹窗
dialog.dismiss();
}
});
dialog.show();
}
private int[] mIcons = new int[]{
R.drawable.shape_address_normal,
R.drawable.shape_address_normal,
R.drawable.shape_address_normal,
R.drawable.shape_address_normal,
R.drawable.shape_address_normal,
R.drawable.shape_address_normal
};
private String[] titles = new String[]{
"半透明1",
"半透明2",
"半透明3",
"半透明4",
"半透明5",
"半透明6"
};
class AddressStyleAdapter extends BaseAdapter {
@Override
public int getCount() {
return titles.length;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = View.inflate(SettingActivity.this, R.layout.item_address, null);
ImageView ivIcon = view.findViewById(R.id.iv_icon);
ImageView ivSelect = view.findViewById(R.id.iv_select);
TextView tvTitle = view.findViewById(R.id.tv_title);
ivIcon.setImageResource(mIcons[position]);
tvTitle.setText(titles[position]);
int pos = PrefUtils.getInt(getApplicationContext(),GlobalConstants.SP_FILE, GlobalConstants.PREF_ADDRESS_STYLE, 0);
if (position == pos) {
ivSelect.setVisibility(View.VISIBLE);
} else {
ivSelect.setVisibility(View.GONE);
}
return view;
}
}
**MSG 06-14 点击条目、记录选中位置 **
代码如上
**MSG 06-15 根据记录样式修改窗口布局背景 **
public void showToast(String text) {
textView.setText(text);
int pos = PrefUtils.getInt(mContext, GlobalConstants.SP_FILE, GlobalConstants.PREF_ADDRESS_STYLE, 0);
textView.setBackgroundResource(mIcons[pos]);
mWM.addView(textView, params);
}
**MSG 06-16 设置弹窗进入退出动画 **
1、在style.xml中设置样式
2、设置动画address_dialog_enter.xml
3、exit反之
4、自定义dialog
package cn.nubia.mobilesecurityguard.view;
import android.app.Dialog;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import cn.nubia.mobilesecurityguard.R;
/**
* 自定义dialog
* 1、给dialog设置布局
* 2、需要清除标题栏
* 3、
*/
public class AddressDialog extends Dialog {
private final ListView lvAddress;
public AddressDialog(Context context) {
super(context,R.style.AddressDialogStyle);
setContentView(R.layout.address_dialog);
//修改dialog所在窗口window的位置
Window window = getWindow();
WindowManager.LayoutParams params = window.getAttributes();
params.gravity = Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL;
window.setAttributes(params);
lvAddress = findViewById(R.id.lv_address);
}
public void setAdapter(BaseAdapter adapter) {
lvAddress.setAdapter(adapter);
}
public void setOnItemClickListener(AdapterView.OnItemClickListener listener) {
lvAddress.setOnItemClickListener(listener);
}
}
**MSG 07-02 黑名单数据库封装 **
1、创建数据库帮助类
package cn.nubia.mobilesecurityguard.db;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class BlackNumberOpenHelper extends SQLiteOpenHelper {
public BlackNumberOpenHelper(Context context) {
super(context, "blacknumber.db",null,1);
}
@Override
public void onCreate(SQLiteDatabase db) {
//创建表
String sql = "create table blacknumber(_id integer primary key autoincrement," +
" number varchar(30),mode integer)";
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
2、创建增删改查dao
package cn.nubia.mobilesecurityguard.db.dao;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import java.util.ArrayList;
import cn.nubia.mobilesecurityguard.bean.BlackNumberInfo;
import cn.nubia.mobilesecurityguard.db.BlackNumberOpenHelper;
/**
* 黑名单
*/
public class BlackNumberDao {
private final BlackNumberOpenHelper helper;
public BlackNumberDao(Context context) {
helper = new BlackNumberOpenHelper(context);
}
public boolean insert(String number, int mode) {
SQLiteDatabase db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("number", number);
values.put("mode", mode);
//返回插入记录的id,-1表示失败
long insert = db.insert("blacknumber", null, values);
db.close();
return insert != -1;
}
public boolean delete(String number) {
SQLiteDatabase db = helper.getWritableDatabase();
//影响的行数,0表示未删除
int delete = db.delete("blacknumber", "number=?", new String[]{number});
db.close();
return delete > 0;
}
public boolean update(String number, int mode) {
SQLiteDatabase db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
//values.put("number", number);
values.put("mode", mode);
//影响的行数
int update = db.update("blacknumber", values, "number=?", new String[]{number});
db.close();
return update > 0;
}
//查询是否在数据库
public boolean query(String number) {
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.query("blacknumber", new String[]{"number", "mode"}, "number=?", new String[]{number}, null, null, null);
boolean isExit = false;
if (cursor != null) {
if (cursor.moveToNext()) {
isExit = true;
}
cursor.close();
}
db.close();
return isExit;
}
//查询某个号码的拦截模式
public int queryMode(String number) {
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.query("blacknumber", new String[]{"mode"}, "number=?", new String[]{number}, null, null, null);
int mode = -1;
if (cursor != null) {
if (cursor.moveToNext()) {
mode = cursor.getInt(0);
}
cursor.close();
}
db.close();
return mode;
}
//查询所有号码的集合
public ArrayList queryAllNumberAndMode() {
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.query("blacknumber", new String[]{"number","mode"}, null, null, null, null, null);
ArrayList list = new ArrayList<>();
if (cursor != null) {
while (cursor.moveToNext()) {
BlackNumberInfo info = new BlackNumberInfo();
String number = cursor.getString(0);
int mode = cursor.getInt(1);
info.number = number;
info.mode = mode;
list.add(info);
}
cursor.close();
}
db.close();
return list;
}
}
3、封装数据对象
package cn.nubia.mobilesecurityguard.bean;
public class BlackNumberInfo {
public String number;
public int mode;
}
**MSG 07-03 单例设计模式 **
将数据库dao改造成单例模式
package cn.nubia.mobilesecurityguard.db.dao;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import java.util.ArrayList;
import cn.nubia.mobilesecurityguard.bean.BlackNumberInfo;
import cn.nubia.mobilesecurityguard.db.BlackNumberOpenHelper;
/**
* 黑名单-单例设计模式
*
* 两种方式进行初始化:
* 1、懒汉式:线程安全问题 1.给方法加同步锁 synchronized 2.创建对象的代码块加同步锁
* //2、公开方法,返回单例对象
* public static BlackNumberDao getInstance(Context context) {
* if (mInstance == null) {
* synchronized (BlackNumberDao.class) {
* if (mInstance == null) {
* mInstance = new BlackNumberDao(context);
* }
* }
* }
* return mInstance;
* }
* 2、饿汉式:
* private static BlackNumberDao mInstance = new BlackNumberDao();
*
*/
public class BlackNumberDao {
private final BlackNumberOpenHelper helper;
//3、声明一个静态对象
private static BlackNumberDao mInstance;
//1、构造方法私有
private BlackNumberDao(Context context) {
helper = new BlackNumberOpenHelper(context);
}
//2、公开方法,返回单例对象
public static BlackNumberDao getInstance(Context context) {
if (mInstance == null) {
synchronized (BlackNumberDao.class) {
if (mInstance == null) {
mInstance = new BlackNumberDao(context);
}
}
}
return mInstance;
}
public boolean insert(String number, int mode) {
SQLiteDatabase db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("number", number);
values.put("mode", mode);
//返回插入记录的id,-1表示失败
long insert = db.insert("blacknumber", null, values);
db.close();
return insert != -1;
}
public boolean delete(String number) {
SQLiteDatabase db = helper.getWritableDatabase();
//影响的行数,0表示未删除
int delete = db.delete("blacknumber", "number=?", new String[]{number});
db.close();
return delete > 0;
}
public boolean update(String number, int mode) {
SQLiteDatabase db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
//values.put("number", number);
values.put("mode", mode);
//影响的行数
int update = db.update("blacknumber", values, "number=?", new String[]{number});
db.close();
return update > 0;
}
//查询是否在数据库
public boolean query(String number) {
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.query("blacknumber", new String[]{"number", "mode"}, "number=?", new String[]{number}, null, null, null);
boolean isExit = false;
if (cursor != null) {
if (cursor.moveToNext()) {
isExit = true;
}
cursor.close();
}
db.close();
return isExit;
}
//查询某个号码的拦截模式
public int queryMode(String number) {
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.query("blacknumber", new String[]{"mode"}, "number=?", new String[]{number}, null, null, null);
int mode = -1;
if (cursor != null) {
if (cursor.moveToNext()) {
mode = cursor.getInt(0);
}
cursor.close();
}
db.close();
return mode;
}
//查询所有号码的集合
public ArrayList queryAllNumberAndMode() {
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.query("blacknumber", new String[]{"number","mode"}, null, null, null, null, null);
ArrayList list = new ArrayList<>();
if (cursor != null) {
while (cursor.moveToNext()) {
BlackNumberInfo info = new BlackNumberInfo();
String number = cursor.getString(0);
int mode = cursor.getInt(1);
info.number = number;
info.mode = mode;
list.add(info);
}
cursor.close();
}
db.close();
return list;
}
}
MSG 07-04 单元测试
package cn.nubia.mobilesecurityguard;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import cn.nubia.mobilesecurityguard.db.dao.BlackNumberDao;
import static android.support.test.InstrumentationRegistry.getContext;
/**
* 黑名单测试:
*
*/
@RunWith(AndroidJUnit4.class)
public class BlackNumerTest{
@Test
public void testAdd() {
BlackNumberDao dao = BlackNumberDao.getInstance(getContext());
dao.insert("110",0);
}
}
MSG 07-05 给黑名单列表填充数据
package cn.nubia.mobilesecurityguard.activity;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ListView;
import java.util.ArrayList;
import cn.nubia.mobilesecurityguard.R;
import cn.nubia.mobilesecurityguard.adapter.BlackNumberAdapter;
import cn.nubia.mobilesecurityguard.bean.BlackNumberInfo;
import cn.nubia.mobilesecurityguard.db.dao.BlackNumberDao;
public class BlackNumberActivity extends AppCompatActivity {
private ListView lvAddNumber;
private BlackNumberDao dao;
private ArrayList list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_black_number);
initView();
initData();
}
/**
* 数据库查询放在子线程
*/
private void initData() {
dao = BlackNumberDao.getInstance(this);
new Thread() {
@Override
public void run() {
list = dao.queryAllNumberAndMode();
runOnUiThread(new Runnable() {
@Override
public void run() {
BlackNumberAdapter blackNumberAdapter = new BlackNumberAdapter(BlackNumberActivity.this, list);
lvAddNumber.setAdapter(blackNumberAdapter);
}
});
}
}.start();
}
private void initView() {
lvAddNumber = findViewById(R.id.lv_add_number);
}
}
MSG 07-06 Gradle刷新问题解决
MSG 07-07 ListView优化
package cn.nubia.mobilesecurityguard.adapter;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import cn.nubia.mobilesecurityguard.R;
import cn.nubia.mobilesecurityguard.bean.BlackNumberInfo;
public class BlackNumberAdapter extends BaseAdapter {
private ArrayList list;
private Context context;
public BlackNumberAdapter(Context context, ArrayList list) {
this.list = list;
this.context = context;
}
@Override
public int getCount() {
return list.size();
}
@Override
public BlackNumberInfo getItem(int position) {
return (BlackNumberInfo) list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
convertView = View.inflate(context, R.layout.item_black_number, null);
holder = new ViewHolder();
holder.tvNumber = convertView.findViewById(R.id.tv_number);
holder.tvMode = convertView.findViewById(R.id.tv_mode);
holder.ivDelete = convertView.findViewById(R.id.iv_delete);
//将holder保存和当前布局绑定
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
BlackNumberInfo info = getItem(position);
holder.tvNumber.setText(info.number);
switch (info.mode) {
case 0:
holder.tvMode.setText("拦截电话");
break;
case 1:
holder.tvMode.setText("拦截短信");
break;
case 2:
holder.tvMode.setText("拦截全部");
break;
}
return convertView;
}
public static class ViewHolder {
public TextView tvNumber;
public TextView tvMode;
public ImageView ivDelete;
}
}
MSG 07-08 加载中布局展示
使用:
llLoading.setVisibility(View.VISIBLE);
isFristUseDb = true;
dao = BlackNumberDao.getInstance(this);
new Thread() {
@Override
public void run() {
if (isFristUseDb) {
for (int i = 0; i < 1000; i++) {
dao.insert("1" + i, 1);
}
isFristUseDb = false;
}
list = dao.queryAllNumberAndMode();
runOnUiThread(new Runnable() {
@Override
public void run() {
BlackNumberAdapter blackNumberAdapter = new BlackNumberAdapter(BlackNumberActivity.this, list);
lvAddNumber.setAdapter(blackNumberAdapter);
llLoading.setVisibility(View.GONE);
}
});
}
}.start();
耗时:
SystemClock.sleep(2000);
MSG 07-09 自定义旋转进度条
custom_loading.xml
MSG 07-10 include标签的使用
单独抽取
MSG 07-11 数据库分页查询sql语句
//分页查询
public ArrayList queryPartNumberAndMode(int index) {
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.rawQuery("select number, mode from blacknumber limit ?,20", new String[]{index + ""});
ArrayList list = new ArrayList<>();
if (cursor != null) {
while (cursor.moveToNext()) {
BlackNumberInfo info = new BlackNumberInfo();
String number = cursor.getString(0);
int mode = cursor.getInt(1);
info.number = number;
info.mode = mode;
list.add(info);
}
cursor.close();
}
db.close();
return list;
MSG 07-12 判读ListView是否滑动到底
lvAddNumber.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
//滑动状态发生变化
if (scrollState == SCROLL_STATE_IDLE) {
//当前显示最后一个条目的位置
int position = lvAddNumber.getLastVisiblePosition();
//当前显示的最后一条的位置等于集合总数的-1,就是滑动到底了
if (position == list.size() - 1) {
Log.e("sunyang","滑动到底了,加载下一页数据");
initData();
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
});
MSG 07-13 加载下一页数据
private void initData() {
llLoading.setVisibility(View.VISIBLE);
isFristUseDb = true;
SystemClock.sleep(2000);
dao = BlackNumberDao.getInstance(this);
new Thread() {
@Override
public void run() {
if (isFristUseDb) {
for (int i = 0; i < 100; i++) {
dao.insert("1" + i, 1);
}
isFristUseDb = false;
}
注意:在第一次加载时集合为空,会出现空指针
list = dao.queryPartNumberAndMode(list.size());
runOnUiThread(new Runnable() {
@Override
public void run() {
BlackNumberAdapter blackNumberAdapter = new BlackNumberAdapter(BlackNumberActivity.this, list);
lvAddNumber.setAdapter(blackNumberAdapter);
llLoading.setVisibility(View.GONE);
}
});
}
}.start();
}
备注:在第一次加载时集合为空,会出现空指针,在开始时
private ArrayList list = new ArrayList<>();
集合追加:
moreList = dao.queryPartNumberAndMode(list.size());
list.addAll(moreList);
不跳页面,加载数据
private void initData() {
llLoading.setVisibility(View.VISIBLE);
isFristUseDb = true;
SystemClock.sleep(2000);
dao = BlackNumberDao.getInstance(this);
new Thread() {
@Override
public void run() {
if (isFristUseDb) {
for (int i = 0; i < 100; i++) {
dao.insert("1" + i, 1);
}
isFristUseDb = false;
}
moreList = dao.queryPartNumberAndMode(list.size());
list.addAll(moreList);
runOnUiThread(new Runnable() {
@Override
public void run() {
if(blackNumberAdapter == null) {
blackNumberAdapter = new BlackNumberAdapter(BlackNumberActivity.this, list);
lvAddNumber.setAdapter(blackNumberAdapter);
} else {
//如果是从第二页开始,就进行数据刷新,不会跳页面
blackNumberAdapter.notifyDataSetChanged();
}
llLoading.setVisibility(View.GONE);
}
});
}
}.start();
}
MSG 07-14 判读是否到达最后 一页
private void initView() {
lvAddNumber = findViewById(R.id.lv_add_number);
llLoading = findViewById(R.id.ll_loading);
lvAddNumber.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
//滑动状态发生变化
if (scrollState == SCROLL_STATE_IDLE) {
//当前显示最后一个条目的位置
int position = lvAddNumber.getLastVisiblePosition();
//当前显示的最后一条的位置等于集合总数的-1,就是滑动到底了
if (position == list.size() - 1) {
Log.e("sunyang","滑动到底了,加载下一页数据");
//判断是否还有数据需要加载
if (list.size() < dao.getCount()) {
initData();
}
} else {
Toast.makeText(BlackNumberActivity.this, "没有数据了", Toast.LENGTH_SHORT).show();
}
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
});
}
MSG 07-15 判断加载完成再加载下页数据
变量控制