快捷键
ctrl+shift+enter 自动补全
ctrl+alt+space 自动补全选中项
shift+enter 转到下一行
startActivityForResult()
((MainActivity) mContext).startActivityForResult(intent, 100);``requestCode
必须大于0 才能在onActivityResult
中监听到
double 和 float 比较大小 问题
如果两个数值相同,但是类型不同,也会判为不同(精度问题) 记住java一定要用double,就算数值不大也要用double。 float放在内存中其实是当作double来处理的 short,char,boolean,byte在内存中都是以int形式来处理的 当然long类型例外,虽然long类型也会增加资源的开销,但是毕竟能完成int完成不了的功能
自定义layout与第三方layout
自定义layout与第三方layout有时会出现重名,导致错误发生
android 9.0 网络网络请求库不能使用http 且不能使用apache
android webview 监听webload finish事件
webview_basewebview.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
@Override
public void onPageFinished(WebView view, String url) {
loadingDialog.dismiss();
}
});
webview 加载本地文件 提高加载速度
由于JS文件过大,加载太慢,所以将JS文件放到本地加载,以提高加载速度 主要是用到了 WebViewClient 的shouldInterceptRequest()方法 本地JS文件,放在assets目录下
mWebView.setWebViewClient(new WebViewClient() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView webView, String url ) {
Uri uri = Uri.parse(url);
String fileName = uri.getLastPathSegment();
if (SPE_BUNDLE_JS.equals(fileName)) {
try {
Map<String, String> map = new HashMap<>();
map.put("Access-Control-Allow-Origin", "*");
map.put("Access-Control-Allow-Headers", "Content-Type");
LogUtils.i("Cordova", "加载本地JS:" + fileName);
WebResourceResponse resourceResponse = new WebResourceResponse("application/javascript", "UTF-8", 200, "", map, mActivity.getAssets().open(SPE_BUNDLE_JS));
return resourceResponse;
} catch (Exception e) {
e.printStackTrace();
return null;//异常情况,直接访问网络资源
}
} else {
LogUtils.i(TAG, "加载服务端JS:" + fileName);
return super.shouldInterceptRequest(webView, url);
}
}
....
}
多线程-AsyncTask 异步任务
syncTask,总结起来就是: 3个泛型,4个步骤。
3个泛型
AsyncTask <Params, Progress, Result>
- Params: 这个泛型指定的是我们传递给异步任务执行时的参数的类型
- Progress: 这个泛型指定的是我们的异步任务在执行的时候将执行的进度返回给UI线程的参数的类型
- Result: 这个泛型指定的异步任务执行完后返回给UI线程的结果的类型
- 在定义一个类继承AsyncTask类的时候,必须要指定好这三个泛型的类型,如果都不指定的话,则都将其写成Void
AsyncTask <Void, Void, Void>
4个步骤
- onPreExecute(): 这个方法是在执行异步任务之前的时候执行,并且是在UI Thread当中执行的,通常我们在这个方法里做一些UI控件的初始化的操作,例如弹出要给ProgressDialog
- doInBackground(Params... params): 在onPreExecute()方法执行完之后,会马上执行这个方法,这个方法就是来处理异步任务的方法,Android操作系统会在后台的线程池当中开启一个worker thread来执行我们的这个方法,所以这个方法是在worker thread当中执行的,这个方法执行完之后就可以将我们的执行结果发送给我们的最后一个 onPostExecute 方法,在这个方法里,我们可以从网络当中获取数据等一些耗时的操作
- onProgressUpdate(Progess... values): 这个方法也是在UI Thread当中执行的,我们在异步任务执行的时候,有时候需要将执行的进度返回给我们的UI界面,例如下载一张网络图片,我们需要时刻显示其下载的进度,就可以使用这个方法来更新我们的进度。这个方法在调用之前,我们需要在 doInBackground 方法中调用一个 publishProgress(Progress) 的方法来将我们的进度时时刻刻传递给 onProgressUpdate 方法来更新
- onPostExecute(Result... result): 当我们的异步任务执行完之后,就会将结果返回给这个方法,这个方法也是在UI Thread当中调用的,我们可以将返回的结果显示在UI控件上 使用:
/**
* Represents an asynchronous login/registration task used to authenticate
* the user.
*/
public class UserLoginTask extends AsyncTask<Void, Void, Boolean> {
private final String mEmail;
private final String mPassword;
UserLoginTask(String email, String password) {
mEmail = email;
mPassword = password;
}
@Override
protected void onPreExecute() {
showProgress(true);
}
@Override
protected Boolean doInBackground(Void... params) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
return false;
}
for (String credential : DUMMY_CREDENTIALS) {
String[] pieces = credential.split(":");
if (pieces[0].equals(mEmail)) {
// Account exists, return true if the password matches.
return pieces[1].equals(mPassword);
}
}
return true;
}
@Override
protected void onPostExecute(final Boolean success) {
mAuthTask = null;
if (success) {
finish();
} else {
showProgress(false);
mPasswordView.setError(getString(R.string.error_incorrect_password));
mPasswordView.requestFocus();
}
}
@Override
protected void onCancelled() {
mAuthTask = null;
showProgress(false);
}
}
使用AsyncTask
Handler 使用
Bundle bundleData = Algorithm(location);
if (bundleData != null) {
LocationHandler locationHandler = new LocationHandler(MapActivity.this);
Message locationMsg = locationHandler.obtainMessage();
bundleData.putParcelable("location", location);
locationMsg.setData(bundleData);
locationHandler.sendMessage(locationMsg);
}
```
## Handler 弱引用处理内存泄漏
```java
public static class LocationHandler extends Handler {
WeakReference<MapActivity> mActivity;
LocationHandler(MapActivity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
MapActivity activity = mActivity.get();
if (activity != null) {
// TODO: 2019年4月17日
}
}
}
推荐使用AppCompatActivity 以及toolbar替代actionBar
toolbar布局 布局文件夹中添加toolbar.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
style="@style/Album.WrapContent.WidthMatchParent"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
其中pupupTheme
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" >
<item name="actionOverflowMenuStyle">@style/OverflowMenuStyle</item>
</style>
<style name="OverflowMenuStyle" parent="Widget.AppCompat.Light.PopupMenu.Overflow">
<item name="overlapAnchor">false</item>
<!--把该属性改为false即可使menu位置位于toolbar之下-->
</style>
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
<style name="Toolbar.Popup" parent="ThemeOverlay.AppCompat.Light">
<item name="actionMenuTextColor">@color/fontDark</item>
<item name="textAllCaps">false</item>
<item name="android:fitsSystemWindows">true</item><!--设置沉浸式-->
</style>
在使用的地方 <include layout="@layout/toolbar" />
就可以有标题栏了
设置返回按钮并监听返回
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home://监听返回
this.finish();
default:
return true;
}
return super.onOptionsItemSelected(item);
}
toolbar title居中
public class Utils {
public static void setTitleCenter(Toolbar toolbar) {
if (toolbar == null) return;
String title = "title";
final CharSequence originalTitle = toolbar.getTitle();
toolbar.setTitle(title);
for (int i = 0; i < toolbar.getChildCount(); i++) {
View view = toolbar.getChildAt(i);
if (view instanceof TextView) {
TextView textView = (TextView) view;
if (title.equals(textView.getText())) {
textView.setGravity(Gravity.CENTER);
Toolbar.LayoutParams params = new Toolbar.LayoutParams(Toolbar.LayoutParams.WRAP_CONTENT, Toolbar.LayoutParams.MATCH_PARENT);
params.gravity = Gravity.CENTER;
textView.setLayoutParams(params);
}
}
toolbar.setTitle(originalTitle);
}
}
}
在初始化时使用Utils.setTitleCenter(toolbar);
就可以了
设置右侧菜单
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_map_menu, menu);
return true;
}
menu布局文件
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<group>
<item
android:id="@+id/menu_map_normal"
android:title="@string/map_menu_normal"
app:showAsAction="never" />
<item
android:id="@+id/menu_map_satellite"
android:title="@string/map_menu_satellite"
app:showAsAction="never" />
<item
android:id="@+id/menu_map_none"
android:title="@string/map_menu_none"
app:showAsAction="never" />
</group>
</menu>
menu菜单监听 同toolbar返回监听
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
this.finish();
case R.id.menu_map_normal:
mBaiduMap.setMapType(BaiduMap.MAP_TYPE_NORMAL);
break;
case R.id.menu_map_satellite:
mBaiduMap.setMapType(BaiduMap.MAP_TYPE_SATELLITE);
break;
case R.id.menu_map_none:
mBaiduMap.setMapType(BaiduMap.MAP_TYPE_NONE);
break;
default:
return true;
}
return super.onOptionsItemSelected(item);
}
获取assets资源
assets文件夹目录与java目录时同级
/**
* 获取Assets 文件内容
* @param fileName 文件名
* @param context context
* @return String
*/
public static String getJson(String fileName, Context context) {
//将json数据变成字符串
StringBuilder stringBuilder = new StringBuilder();
try {
//获取assets资源管理器
AssetManager assetManager = context.getAssets();
//通过管理器打开文件并读取
BufferedReader bf = new BufferedReader(new InputStreamReader(assetManager.open(fileName)));
String line;
while ((line = bf.readLine()) != null) {
stringBuilder.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
return stringBuilder.toString();
}
gosn解析json
Gson gson=new Gson();
Bean data=gson.fromJson(string,Bean.class);//Bean时定义的实体类
// 从assets中获取json内容并解析
//json内容为
/*
[{"center":[116.41338310930209,37.60701416950952],"id":"0-0"}] //注:类型为Bean
*/
try {
JSONArray gridDataList = new JSONArray(Utils.getJson("map_grid_data.json", HomeActivity.this));//获取JSONArray
Type listType = new TypeToken<List<Bean>>() {
}.getType();//获取类型
List<Bean> list = new Gson().fromJson(gridDataList.toString(), listType);
Log.d("HomeActivity", list.toString());
} catch (JSONException e) {
e.printStackTrace();
}
String[] 转 List<String>
String[] listItems = new String[]{
"消息类型对话框(蓝色按钮)",
"消息类型对话框(红色按钮)"
};
List<String> data = new ArrayList<>();
Collections.addAll(data, listItems);
QMUI 配置
1.theme
2.dialog context
Timer 及 Timer取消
mTimer = new Timer();
mTimerTask = new TimerTask() {
@Override
public void run() {
if (isFinished) return;
runOnUiThread(new Runnable() {
@Override
public void run() {
if (mTipDialog != null) {
mTipDialog.show();
}
}
});
}
};
mTimer.schedule(mTimerTask, 400);
注意: 当acivity已经finish时,timer还没有执行,此时就不能再显示view了,需要再onDestroy中添加变量,监听是否已经finish
取消timer
if (mTimer != null) {
mTimer.cancel();
mTimer.purge();//清理timer
mTimer = null;
}
if (mTimerTask != null) {
mTimerTask.cancel();
mTimerTask = null;
}
drawable资源文件尺寸
L DPI ( Low Density Screen,120 DPI ),其图标大小为 36 x 36 px
M DPI ( Medium Density Screen, 160 DPI ),其图标大小为 48 x 48 px
H DPI ( High Density Screen, 240 DPI ),其图标大小为 72 x 72 px
XH DPI ( Extra-high density screen, 320 DPI ),其图标大小为 96 x 96 px
XXH DPI( xx-high density screen, 480 DPI ),其图标大小为144 x 144 px
XXXH DPI( xxx-high density screen, 640 DPI ),其图标大小为192 x 192 px
assets 权限 android9.0
fragment 使用
menu显示图片和文字
异步任务处理方式:
1.AsyncTask 2.AsyncQueryHandler 3.Thread + Handler (Looper) 4.Timer,TimerTask 5.HandlerThead 6.LoaderCallbacks
如果handler不是在子线程中定义,handler.post()方法实际上还是在主线程中执行 AsyncQueryHandler,HandlerThread则都是另起一个子线程去进行数据处理(对于两者的使用区别,如果我们只需要进行一次异步查询,如加载通话记录,使用AsyncQueryHandler更为方便。如果需要一个工作者线程,则建议使用Handler。ThreadHandlerThread通过其mLooper对象实现了对消息队列处理逻辑的简易封装) AsyncTask则适合在进行后台操作时需要更新UI时的场景 每个Activity和fragment都对应一个LoaderManager
gradlle 的buildTypes内部配置
示例:
buildTypes {
debug {
// 服务器配置
buildConfigField "String", "SERVERHEAD","\"http://110.11.11.00/miaomiao/\""
//是否混淆 默认为false
minifyEnabled false
//是否对齐app所有资源
zipAlignEnabled true
//移除无用的resource文件
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
signingConfig signingConfigs.debug
}
release {
// 正式服务器
buildConfigField "String", "SERVERHEAD","\"http://miaomiao.com/miaomiao/\""
//混淆
minifyEnabled true
zipAlignEnabled true//内存对齐
// 移除无用的resource文件
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
//签名
signingConfig signingConfigs.release
}
}
可以直接在android{}内配置签名信息,代码如下:
signingConfigs {
debug {
//miaomiao_keystore.jks文件最好放在项目app目录下
storeFile file("miaomiao_keystore.jks")
storePassword "123456"
keyAlias "miaomiao"
keyPassword "123456"
}
release {
//miaomiao_keystore.jks文件最好放在项目app目录下
storeFile file("miaomiao_keystore.jks")
storePassword "123456"
keyAlias "miaomiao"
keyPassword "123456"
}
}
buildConfigField 字段参数配置,android的gradle会根据buildTypes的配置自动生成BuildConfig.java文件,直接在项目接口配置的java文件内通过类名调用获取,可以避免每次调试与正式打包时容易忘记切换服务器的问题,省去出错麻烦 使用buildConfigField
//服务器头字段
public static final String SERVER_HEADER = BuildConfig.SERVERHEAD;
以下一般为默认属性即可:
debuggable: debug模式默认为true, release模式默认是false
jniDebuggable: debug模式与release模式默认都是false
renderscriptDebuggable: debug模式与release模式默认都是false
StrictMode(严格模式)的使用
StrictMode严格模式,主要用来检测程序中违例情况的开发者工具。最常用的场景就是检测主线程中本地磁盘、网络读写等耗时的操作以及Activity泄露等,但该模式不建议在Release版本开启,此外该模式无法监控JNI中的磁盘IO和网络请求且其违例情况仅供参考,需结合实际开发需求予以解决。 封装
public class DebugUtil {
public static void startStrictModeVmPolicy(){
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectActivityLeaks()/*检测Activity内存泄露*/
.detectLeakedClosableObjects()/*检测未关闭的Closable对象*/
.detectLeakedSqlLiteObjects() /*检测Sqlite对象是否关闭*/
/*也可以采用detectAll()来检测所有想检测的东西*/
.penaltyLog().build());
}
public static void startStrictModeThreadPolicy(){
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()/*磁盘读取操作检测*/
.detectDiskWrites()/*检测磁盘写入操作*/
.detectNetwork() /*检测网络操作*/
/*也可以采用detectAll()来检测所有想检测的东西*/
.penaltyLog().build());
}
}
需要查看TAG为StrictMode的日志即可,如:logcat -c;logcat -s StrictMode 或者adb logcat | grep StrictMode
- 获取当前context的方法
getApplicationContext()
注解 butterknife
AutoCompleteTextView MultiAutoCompleteTextView
Notification
定位错误方法
在项目root路径,命令行cmd输入:
gradlew processDebugResources --debug
//打印到txt文件
gradlew processDebugResources --debug > ./debug.txt