请求位置权限:如果您同时使用NETWORK_PROVIDER和GPS_PROVIDER,则只需要请求ACCESS_FINE_LOCATION权限,大权限包含小权限
在Android6.0开始,App可以直接安装,App在运行时一个一个询问用户授予权限,这个对话框不是开发者调用某个权限的功能时由系统自动弹出,而是需要开发者手动调用,如果你直接调用而没有去申请权限的话,将会导致App奔溃!!!
不涉及用户隐私的, 只需要在Manifest中声明即可,比如网络、蓝牙、NFC等;
涉及到用户隐私信息的, 需要用户授权后才可使用,比如SD卡读写、联系人、短信读写等。()
此类权限也必须在Manifest中申明,否则申请时不提使用用户,直接回调开发者权限被拒绝
生成tag: logt
打印日志 Log.v() Log.d() Log.i() Log.w() 以及 Log.e()
根据首字母对应VERBOSE,DEBUG,INFO, WARN,ERROR
BUG : 过多子线程抢占资源导致程序崩溃
完美请求权限,解决用户永久拒绝,多次拒绝等问题
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
locationText = (TextView) findViewById(R.id.Location);
locationText.setText("..........");
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) {
Log.e("1", "shouldShowRequestPermissionRationale() 返回 true, 即上次弹出权限点击了禁止(但没有勾选“下次不在询问”),则重新请求权限");
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
} else {
Log.e("1", "shouldShowRequestPermissionRationale() 返回 false ,即第一次打开App时,重新请求权限");
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
}
}else {
Log.e("1", "已经有权限了");
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
}
}
@SuppressLint("MissingPermission")
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
Toast.makeText(this, "onRequestPermissionsResult", Toast.LENGTH_LONG).show();
switch (requestCode) {
case 1: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.e("1", "权限被用户同意,可以去放肆了");
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
} else {
Log.e("1", "权限被用户拒绝了,洗洗睡吧");
Boolean notFoeverRefused = ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION);
if (notFoeverRefused) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
} else {
Log.e("1", "shouldShowRequestPermissionRationale() 返回 false ,上次选择禁止并勾选【下次不在询问】" +
"现在我们唯一能做的就是跳转到我们 App 的设置界面,让用户手动开启权限了。");
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivityForResult(intent, 1);
}
}
return;
}
}
}
@SuppressLint("MissingPermission")
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode==1){
Log.e("1", "onActivityResult");
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) {
Log.e("1", "shouldShowRequestPermissionRationale() 返回 true, 即上次弹出权限点击了禁止(但没有勾选“下次不在询问”),则重新请求权限");
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
} else {
Log.e("1", "shouldShowRequestPermissionRationale() 返回 false ,即第一次打开App时,重新请求权限");
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
}
}else {
Log.e("1", "已经有权限了");
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
}
}
}

- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
implements View.OnClickListener
private Button login;
login = (Button) findViewById(R.id.login);
@Override
public void onClick(View v) {
switch(v.getId()){
case R.id.buttonDW:
break;
}
Toast.makeText().show();
startActivity(new Intent(this,MapActivity.class));
- 服务器传过来的,图片是二进制的
- 把string 的response用getBytes方法获得位流 http://blog.csdn.net/ameyume/article/details/6528205
- 一种是先转换为byte[],再生成bitmap;
- 一种是直接用InputStream生成bitmap。
开个子线程(不能传消息到UI线程),不推荐
new Thread(new Runnable(){
public void run() {}
}).start();
class Anomymous implements Runnable {
public void run() {
}
}
Runnable rn = new Anomymous();
AsyncTask(推荐)
ReverseTask reverseTask = new ReverseTask();
reverseTask.execute(text);
class ReverseTask extends AsyncTask<String,Integer,String> {
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
安卓原生的 Thread (Looper)
对于一些普通的程序开发工作而言,只需要了解Handler的用法即可,几乎接触不到Looper
Handler 在它关联的looper线程中处理异步消息:当发出一个消息之后,首先进入一个MessageQueue,发送消息的函数即刻返回,
而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。
这种机制通常用来处理相对耗时比较长的操作。
Looper.myLooper()
ThreadLocal
public class Thread1 extends Thread {
@Override
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
}
};
Looper.loop();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
HandlerThread: 简化的 Thread (Looper)
HandlerThread.start();
Looper loop =HandlerThread.getLooper();
mSubThreadHandler = new Handler(loop){
public void handleMessage(Message msg) {
}
}
Looper.prepare()
Looper.loop()
连接到外部模拟器
adb connect 127.0.0.1:5555
adb devices
输出调试日志
Log.v(tag,message)
正确地子线程中修改UI
Activity.runOnUiThread(Runnable)
View.postDelayed(Runnable, long)
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
mImageView.post(new Runnable() {
public void run() {
mImageView.setImageBitmap(bitmap);
}
});
}
}).start();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- RxJava + Retrofit + okHttp -网络请求最流行的方式
- Retrofit 负责请求的数据/结果,使用接口的方式呈现
- OkHttp 负责请求的过程
- RxJava 负责异步,各种线程之间的切换,比原生的简洁。
compile 'io.reactivex:rxjava:1.1.5'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
implementation 'com.squareup.okhttp3:okhttp:3.9.1'
compile 'com.jakewharton:butterknife:8.8.1'
SharedPreferences:一个轻量级的存储类,特别适合用于保存软件配置参数。(是用xml文件存放数据,文件存放在/data/data/<package name>/shared_prefs目录下)
安卓单元测试的分类:
本地测试(Local tests): 只在本地机器JVM上运行,以最小化执行时间,这种单元测试不依赖于Android框架,或者即使有依赖,也很方便使用模拟框架来模拟依赖,以达到隔离Android依赖的目的,模拟框架如google推荐的[Mockito][1];
仪器化测试(Instrumented tests): 在真机或模拟器上运行的单元测试,由于需要跑到设备上,比较慢,这些测试可以访问仪器(Android系统)信息,比如被测应用程序的上下文,一般地,依赖不太方便通过模拟框架模拟时采用这种方式。
app/src
├── androidTestjava (仪器化单元测试、UI测试)
├── main/java (业务代码)
└── test/java (本地单元测试)
var isExistPeopleName=false; var peopleNameId='';
function isExist(name)
{
var jsonObj;
fetch("https://api2.bmob.cn/1/classes/position/",
{
headers:
{
'X-Bmob-Application-Id': 'ae69ae4ad1b9328f1993c62a637454a7',
'X-Bmob-REST-API-Key': '05d377b293e63f9f9e22788154af1449',
},
method: 'GET',
}).then(response => response.json()).then(function (result)
{
jsonObj = result;
}).then(() =>
{ isExistPeopleName=false;
for (var i = 0; i < jsonObj.results.length; i++)
{
if (jsonObj.results[0].name == name)
{
isExistPeopleName=true;
peopleNameId=jsonObj.results[0].objectId;
break;
}
}
}
);
}
isExist('123');
undefined
isExistPeopleName;
true
peopleNameId;
"b5bbd688b1"
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
android:launchMode="singleTask"
SingleTop 通知消息打开的页面;登录页面
SingleTask 浏览器、微博等页面(大多数App的主页)
SingleInstance 呼叫来电界面,整个手机操作系统里面只有一个实例存在。
ImageView控件上下留白原因与解决:在图片ImageView控件中添加属性android:adjustViewBounds="true"即可,既保持宽高比
安卓用jdbc操作oracle数据库:要用classes12.jar作为jdbc驱动包,用传统的Class.forName来加载,不能用OracleDataSource这些用了JNDI的方法来加载,其他包太新,都要依赖jdk库!
安卓不能用jdk的库!因为安卓虚拟机改造过了!
Java有很多第三方库。Android的Dalvik虚拟器不完全是Java,也就是Android SDK和传统的SDK不完全一样,如果Java库兼容Android,则可被利用。
Java库使用受限于下面因素:
目标平台:Java代码适配版本是否比Android基于的Java版本更高。是否用了Android不支持的Java SE的API,例如Swing、AWT图形。
Size:为桌面或者服务器设计的Java代码不需要考虑存储和内存空间,Android需要,使用第三方Java代码,可能会是应用Size无法容忍。
性能:Java代码是否要消耗Android设备所能提供的CPU。
界面:Java代码是否需要console界面,或者可以包装我们自己界面中的单纯API。
Android Devcie Monitor:查看手机文件,若显示空白,则 cmd执行 adb root #获取root权限
Android中XML的命名空间(xmlns):
compile 'com.android.support:support-v4:22.1.0'
compile 'com.android.support:recyclerview-v7:22.1.0'
compile 'com.android.support:appcompat-v7:22.1.0'
R文件的位置:app-build-generated-source-r-debug-packageName-R.java
style.xml 可设置应用的主题和颜色,被引用
旋转屏幕,Android就会销毁当前activity,然后再创建新的activity。
在异常的时候自动在异常处生成断点:
Run → View Breakpoints.,选择下拉列表中的Java Exception Breakpoints选项。
接下来选择要捕捉的异常类型。输入 RuntimeException ,按提示选择RuntimeException(java.lang)
返回上一个Activiey: xml设置其 parentActivityName即可 , eg: [.ParentActivity]
fragment :要委托工作任务给托管activity(Fragment只做它该做的事,其他让Activity干)
通常的做法是由fragment定义名为 Callbacks 的回调接口,实现Callbacks接口和其方法,而后Fragment就可以调用它让Activity干事.
<string name="crime_report">%1$s! %2$s. %3$s, and %4$s</string>
String report=getString(R.string.crime_report,'arg1','arg2','arg3','arg4');
android:orientation="vertical" 时, 只有水平方向的设置才起作用,垂直方向的设置不起作用。即:left,right,center_horizontal 是生效的。
android:orientation="horizontal" 时, 只有垂直方向的设置才起作用,水平方向的设置不起作用。即:top,bottom,center_vertical 是生效的。
android:gravity: 这个是针对控件里的元素来说的
android:layout_gravity: 控件的父控件中的位置
ProGuard 为了很好的保护Java源代码,我们需要对编译好后的class文件进行混淆。
可以看到在Android studio3.0中,compile依赖关系已被弃用,被implementation和api替代,provided被compile only替代,apk被runtime only替代。
*.jar, Java Archive File,包含了class 文件与清单文件
*.arr,包含所有资源,class 文件以及 res 资源文件。
*.so ,文件是 unix 的动态连接库,二进制文件。SO 库本身不参与 APK 的编译过程,使用 JNI 调用 SO 库里的 Native 方法,进行NDK开发。
/lib/<abi>/lib<name>.so #系统在预期位置找不到原生共享库,便无法加以使用
在胖 APK 中,每个库位于名称与相应 ABI 匹配的目录下。 例如,胖 APK 可能包含:
/lib/armeabi/libfoo.so
/lib/armeabi-v7a/libfoo.so
/lib/arm64-v8a/libfoo.so
/lib/x86/libfoo.so
/lib/x86_64/libfoo.so
HttpClient在sdk 23中不再支持!!! #你必须使用URLConnection或OkHttp或降级到sdk 22(compile 'com.android.support:appcompat-v7:22.2.0')
adb uninstall test.alexander.ru.testapp
allprojects {
repositories {
jcenter()
google()
}
}
AbsoluteLayout, DrawerLayout, CoordinatorLayout,GridLayout, FrameLayout, LinearLayout, RelativeLayout, SlidingPaneLayout,SwipeRefreshLayout,
AdapterView <T extends Adapter >,
FragmentBreadCrumbs, LinearLayoutCompat, PagerTitleStrip, RecyclerView, SlidingDrawer, 工具栏, TvView, ViewPager
TabLayout (FrameLayout子类):左右滑动切换页面 #或 ViewPager
CoordinatorLayout:(上滑自动隐藏工具栏)
让工具栏(AppBarLayout里嵌入Toolbar) 能和RecyclerView通信,它只提供触控层,布局还是得再加个其他Layout来
DrawerLayout:用来全局放侧滑导航栏用
compile 'me.imid.swipebacklayout.lib:library:1.1.0'
SdkVersion 对应 安卓版本
Android Support v4: SDK4 对应 安卓1.6及更高版本 ,这个包是使用最广泛的
Android Support v7: SDK7 对应 安卓2.1及以上版本 ,但不包含更低(v7依赖v4)
com.android.support:appcompat-v7 支持库: 一些库旨在与 Android 2.3(API 级别 9)及更高版本搭配使用。
android.support.design: Android的material design兼容库

- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
mAdapter = new MyAdapter(datas);
StaggeredGridLayoutManager mLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));
mRecyclerView.setAdapter(mAdapter);
class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>
@Override
onCreateViewHolder
onBindViewHolder(final ListViewHolder holder, int position)
getItemCount
static class ViewHolder extends RecyclerView.ViewHolder{
mMAdapter.notifyDataSetChanged();
mAdapter.setOnItemClickListener(new OnItemClickListener(){
@Override
public void onItemClick(View view , int position){
Toast.makeText(MainActivity.this, data[position], 600).show();
}
});
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> implements View.OnClickListener{
private String[] datas;
public MyAdapter(String[] datas) {
this.datas = datas;
}
public static interface OnItemClickListener {
void onItemClick(View view , int position);
}
private OnItemClickListener mOnItemClickListener = null;
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false);
ViewHolder vh = new ViewHolder(view);
view.setOnClickListener(this);
return vh;
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
viewHolder.mTextView.setText(datas[position]);
viewHolder.itemView.setTag(position);
}
@Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(v,(int)v.getTag());
}
}
public void setOnItemClickListener(OnItemClickListener listener) {
this.mOnItemClickListener = listener;
}
}

- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65