日期: 2021 年 4 月 29 日

混淆Android的jar包

混淆Android的jar包

现在项目需要做一个sdk提供给开发商,今天在打包jar并混淆的时候找了很久,网上很多就是混淆apk的,马上切入正题

1.导出 Jar

这个直接在Project上Export就行了

2. 运行 Android SDK 里面的 proguardgui.bat

在Android SDK 目录 \tools\proguard\bin 下,如图:

%title插图%num

然后我们需要录入对我们的jar混淆的配置信息

 

-dontwarn 
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
 
-injars
'E:\jar.jar'  #需要混淆的jar文件和路径

-outjars
'E:\myjar_out.jar'  #混淆后的jar文件名字和路径
 
#jar依赖的包
-libraryjars
'D:\workspace\SkyseaSDK\libs\alipay_msp.jar'
-libraryjars
'D:\workspace\SkyseaSDK\libs\android-support-v4.jar'
-libraryjars
'D:\adt-bundle-windows-x86-20130729\platforms\android-17\android.jar'
 
#不参加混淆的类
-keep
public class com.skysea.app.Matrix
 {
    public <fields>;
    public <methods>;
}

-keep public class * extends android.app.Activity 
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider

#不参加混淆的package
-keep class com.skysea.alipay.**

 

将以上配置信息保存在mycfg.pro中,保存前记得把 上面#后的中文删除或者改成英文

那么现在就可以在proguardgui上使用这个配置文件了,LoadConfiguration->mycfg.pro 然后一直next就成功了

WebView加载网页不显示图片解决办法

对于大家来讲WebView肯定很熟悉,因为我们在日常开发中经常用到它。所以对于它的一些基本用法我就不在这啰嗦了,直接进入正题。

我遇到的问题就是在使用WebView加载网页的时候图片不显示(我手机系统是5.1.1),当时出现这个问题我就想当然的以为,是不是给WebView少设置的什么东西。然后百度一下:
mWebview.getSettings().setJavaScriptEnabled(true);//启用js
mWebview.getSettings().setBlockNetworkImage(false);//解决图片不显示

然后我检查了自己代码,这两句话也明明加了啊,这到底是什么鬼。后面我也加过其它的一些设置,依然没有用。难道是我的访问路径有问题吗,于是我随便找了带图片的网页,使用WebView加载了一下,哎呦我擦,图片显示没毛病啊。看来还真是我的访问路径有问题啊,但是别的都显示没问题,为什么就图片不显示呢。我跟踪断点把访问的路径复制了出来一看,我靠原来访问路径是https的呀。于是我就把矛头指向了https,简单来说,https就是http的安全版,它在http的基础上加入了ssl层。https协议在使用的时候需要申请一个安全证书,我就想是不是安全证书有问题,回头一想假如安全证书有问题,页面应该是直接显示空白才对。问题又出在哪,于是我把网页路径复制到了浏览器打开,然后查看了一下网页源码,发现图片的引用是http的,问题会不会就出在这呢。果然不出所料:


if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP) settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);


于是在设置WebView的时候加上了这句话,果然问题解决了。在5.0以下的系统即使不加这句话,图片也可以正常显示,亲测有效。

Android WebView填坑记录

前言

在应用程序开发过程中,经常会采用webview来展现某些界面,这样就可以不受发布版本控制,实时更新,遇到问题可以快速修复。

但是在Android开发中,由于Android版本分化严重,每一个版本针对webview都有部分更改,因此在开发过程中会遇到各种各样的坑,因此在此总结一下在开发过程中遇到的一些坑!

样例

这里不是讲解怎么进行webview开发,而是只罗列其中遇到的一些坑!为了展示这些问题,我们还是写一个样例来进行展开。

样例代码:

  1. /**
  2. * WebView demo
  3. */
  4. public class WebViewActivity extends AppCompatActivity {
  5. public static void start(Context context) {
  6. Intent intent = new Intent();
  7. intent.setClass(context, WebViewActivity.class);
  8. context.startActivity(intent);
  9. }
  10. private static final String TAG = “WebViewActivity”;
  11. public static final String URL = “http://www.163.com”;
  12. private static final int REQUEST_CODE_GET_LOCAL_FILE = 0;
  13. private static final int REQUEST_CODE_GET_LOCAL_FILE_NEW = 1;
  14. private WebView webView;
  15. @Override
  16. protected void onCreate(Bundle savedInstanceState) {
  17. super.onCreate(savedInstanceState);
  18. setContentView(R.layout.activity_web_view);
  19. findViews();
  20. initWebView();
  21. }
  22. /**
  23. * 查找界面view
  24. */
  25. private void findViews() {
  26. webView = (WebView) findViewById(R.id.web_view);
  27. }
  28. /**
  29. * 初始化webview参数
  30. */
  31. private void initWebView() {
  32. initWebSetting();
  33. setAcceptThirdPartyCookies();
  34. initWebClient();
  35. loadUrl();
  36. }
  37. /**
  38. * 加载地址
  39. */
  40. private void loadUrl() {
  41. webView.loadUrl(URL);
  42. }
  43. /**
  44. * 设置WebSetting
  45. */
  46. private void initWebSetting() {
  47. WebSettings webSettings = webView.getSettings();
  48. webSettings.setJavaScriptEnabled(true);
  49. webSettings.setDomStorageEnabled(true);
  50. webSettings.setLoadWithOverviewMode(true);
  51. webSettings.setUseWideViewPort(true);
  52. webSettings.setRenderPriority(WebSettings.RenderPriority.HIGH);
  53. webSettings.setUserAgentString(webSettings.getUserAgentString() + ” VersionCode/” + InstallUtil
  54. .getVersionCode(this));
  55. webSettings.setAppCacheMaxSize(1024 * 1024 * 8);
  56. webSettings.setAllowFileAccess(true);
  57. webSettings.setAppCacheEnabled(true);
  58. webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
  59. webSettings.setSupportZoom(true);
  60. webSettings.setGeolocationEnabled(true);
  61. webSettings.setDatabaseEnabled(true);
  62. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  63. webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
  64. }
  65. }
  66. /**
  67. * 设置跨域cookie读取
  68. */
  69. public final void setAcceptThirdPartyCookies() {
  70. //target 23 default false, so manual set true
  71. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  72. CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);
  73. }
  74. }
  75. /**
  76. * 设置client
  77. */
  78. private void initWebClient() {
  79. webView.setWebChromeClient(new WebChromeClient() {
  80. @Override
  81. public void onProgressChanged(WebView view, int newProgress) {
  82. super.onProgressChanged(view, newProgress);
  83. Log.e(TAG, “onProgressChanged newProgress=” + newProgress);
  84. }
  85. @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  86. @Override
  87. public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
  88. FileChooserParams fileChooserParams) {
  89. uploadFileNew = filePathCallback;
  90. Intent intent = fileChooserParams.createIntent();
  91. try {
  92. startActivityForResult(intent, REQUEST_CODE_GET_LOCAL_FILE_NEW);
  93. } catch (Exception e) {
  94. ToastUtil.showLong(getString(R.string.choose_fail));
  95. return false;
  96. }
  97. return true;
  98. }
  99. //
  100. // FILE UPLOAD <3.0
  101. //
  102. public void openFileChooser(ValueCallback<Uri> uploadFile) {
  103. chooseFile(uploadFile, null, null);
  104. }
  105. public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType) {
  106. chooseFile(uploadFile, acceptType, null);
  107. }
  108. /**
  109. * 4.x
  110. * @param uploadFile
  111. * @param acceptType
  112. * @param capture
  113. */
  114. public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {
  115. chooseFile(uploadFile, acceptType, capture);
  116. }
  117. });
  118. webView.setWebViewClient(new WebViewClient() {
  119. @Override
  120. public void onPageStarted(WebView view, String url, Bitmap favicon) {
  121. super.onPageStarted(view, url, favicon);
  122. Log.e(TAG, “onPageStarted”);
  123. }
  124. @Override
  125. public void onPageFinished(WebView view, String url) {
  126. super.onPageFinished(view, url);
  127. Log.e(TAG, “onPageFinished”);
  128. }
  129. @Override
  130. public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
  131. super.onReceivedError(view, request, error);
  132. Log.e(TAG, “onReceivedError”);
  133. }
  134. @Override
  135. public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
  136. super.onReceivedSslError(view, handler, error);
  137. Log.e(TAG, “onReceivedSslError”);
  138. }
  139. @Override
  140. public boolean shouldOverrideUrlLoading(WebView view, String url) {
  141. Log.e(TAG, “shouldOverrideUrlLoading url=” + url);
  142. return super.shouldOverrideUrlLoading(view, url);
  143. }
  144. });
  145. }
  146. private ValueCallback<Uri[]> uploadFileNew;
  147. private ValueCallback<Uri> uploadFile;
  148. /**
  149. * 文件选择
  150. *
  151. * @param uploadFile
  152. * @param acceptType
  153. * @param capture
  154. */
  155. private void chooseFile(ValueCallback<Uri> uploadFile, String acceptType, String capture) {
  156. Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
  157. if (TextUtils.isEmpty(acceptType)) {
  158. acceptType = “*/*”;
  159. }
  160. intent.setType(acceptType);
  161. this.uploadFile = uploadFile;
  162. try {
  163. startActivityForResult(Intent.createChooser(intent, capture), REQUEST_CODE_GET_LOCAL_FILE);
  164. } catch (Throwable tr) {
  165. tr.printStackTrace();
  166. }
  167. }
  168. @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  169. @Override
  170. public void onActivityResult(int requestCode, int resultCode, Intent data) {
  171. super.onActivityResult(requestCode, resultCode, data);
  172. switch (requestCode) {
  173. case REQUEST_CODE_GET_LOCAL_FILE:
  174. if (uploadFile != null) {
  175. uploadFile.onReceiveValue((resultCode == Activity.RESULT_OK && data != null) ? data.getData() :
  176. null);
  177. uploadFile = null;
  178. }
  179. break;
  180. case REQUEST_CODE_GET_LOCAL_FILE_NEW:
  181. if (uploadFileNew != null) {
  182. uploadFileNew.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, data));
  183. uploadFileNew = null;
  184. }
  185. break;
  186. }
  187. }
  188. }

 

 

布局代码

  1. <?xml version=”1.0″ encoding=”utf-8″?>
  2. <RelativeLayout
  3. xmlns:android=“http://schemas.android.com/apk/res/android”
  4. xmlns:tools=“http://schemas.android.com/tools”
  5. android:layout_width=“match_parent”
  6. android:layout_height=“match_parent”
  7. tools:context=“com.sunny.demo.webview.WebViewActivity”>
  8. <WebView
  9. android:id=“@+id/web_view”
  10. android:layout_width=“match_parent”
  11. android:layout_height=“match_parent”>
  12. </WebView>
  13. </RelativeLayout>

 

填坑

1,回调不成对

在WebView加载地址时,在加载过程中回调次数不一定相同,比如这里我们我们加载http://www.163.com。

  1. E/WebViewActivity: onProgressChanged newProgress=10
  2. E/WebViewActivity: onPageStarted
  3. E/WebViewActivity: shouldOverrideUrlLoading url=http://3g.163.com/
  4. E/WebViewActivity: onPageStarted
  5. E/WebViewActivity: onPageStarted
  6. E/WebViewActivity: shouldOverrideUrlLoading url=http://3g.163.com/touch/
  7. E/WebViewActivity: onProgressChanged newProgress=50
  8. E/WebViewActivity: onPageFinished
  9. E/WebViewActivity: onProgressChanged newProgress=52
  10. E/WebViewActivity: onProgressChanged newProgress=90
  11. E/WebViewActivity: onProgressChanged newProgress=95
  12. E/WebViewActivity: onProgressChanged newProgress=100
  13. E/WebViewActivity: onPageFinished

 

从上面的日志中可以看到onPageStarted与onPageFinished次数不一致,因此如果你在start中进行进度条加载处理,finish中结束,会导致进度条一直不消失。因此可以在onProgressChanged进行处理。当newProgress为100时表示页面加载完成。

2,文件选择回调函数更改

如果你是web页面开发人员,如果要选取本地文件,可以采用input标签,如下一个简单的html页面:

  1. <!DOCTYPE html>
  2. <html lang=“en”>
  3. <head>
  4. <meta charset=“UTF-8”>
  5. <title>Document</title>
  6. </head>
  7. <body>
  8. <input type=“file” accept=“image/*;” capture=“camera” >
  9. <a href=“test1.html”>url替换为test1页面</a>
  10. <script>
  11. var a = function() {
  12. //alert(1);
  13. };
  14. document.addEventListener(‘JSBridgeReady’,a);
  15. </script>
  16. </body>
  17. </html>

 

附录
如果你不是专业的web开发人员,但是又想进行web页面调试,可以自己搭建一个web服务,如果你采用的是MAC可以采用如下方案进行web服务搭建

1.用node装一个http-server

2.brew install node

3.npm install -g http-server 安装

4.在一个目录下 http-server启动就是一个简单http服务里 port 8080

上面的代码加载后效果如下:
这里写图片描述

很简单吧!是不是想说so easy!!!那我们就点击一下选择文件吧!

咦没效果
换手机,好,有效果
再换,咦,又没有效果

是不是感觉很抓狂?这是由于Android的不同版本,WebView的回调函数都不一致。怎么解决这个问题?可以复写不同的回调函数,这里主要有以下四个回调函数,分别处理不同的版本:

  1. @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  2. @Override
  3. public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
  4. FileChooserParams fileChooserParams) {
  5. Intent intent = fileChooserParams.createIntent();
  6. try {
  7. startActivityForResult(intent, REQUEST_CODE_GET_LOCAL_FILE_NEW);
  8. } catch (Exception e) {
  9. ToastUtil.showLong(getString(R.string.choose_fail));
  10. return false;
  11. }
  12. return true;
  13. }
  14. //
  15. // FILE UPLOAD <3.0
  16. //
  17. public void openFileChooser(ValueCallback<Uri> uploadFile) {
  18. chooseFile(uploadFile, null, null);
  19. }
  20. public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType) {
  21. chooseFile(uploadFile, acceptType, null);
  22. }
  23. /**
  24. * 4.x
  25. * @param uploadFile
  26. * @param acceptType
  27. * @param capture
  28. */
  29. public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {
  30. chooseFile(uploadFile, acceptType, capture);
  31. }

 

复写了上述四个函数,同时记得onActivityResult进行返回结果处理。就算是这样,我也不敢保证所有的手机都能正确的进行处理。
刚才在html中是否看到了一个一段document.addEventListener(‘JSBridgeReady’,a)这样的代码。这里是想说给出另外一种解决方案,可以采用JSBridge方式进行web页面与native页面交互,这样就可以屏蔽版本中间的差异。

3,跨域cookie读取

什么是跨域,简单的说就是不同的域名,我们都知道在pc上我们用浏览器访问网址,不同的网址都会在本地存储一些cookie信息,这样就可以实现比如自动登录等功能,在pc上不同域名是不能相互读取其他域下的cookie信息的(非web专业开发人员,如果理解有误,欢迎指出)。

但是在Android上在api 23之前,是可以跨域读取cookie的,比如A域写入一个userId的cookie,B域可以读取该值。但是在23时,系统将该值设置成了false,不再让跨域读取了。如果你的应用有跨域读取需求,怎么办?可以采用如下方式进行开启:

  1. /**
  2. * 设置跨域cookie读取
  3. */
  4. public final void setAcceptThirdPartyCookies() {
  5. //target 23 default false, so manual set true
  6. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  7. CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);
  8. }
  9. }

 

这里我们来看看setAcceptThirdPartyCookies的注释:

Sets whether the {@link WebView} should allow third party cookies to be set.
Allowing third party cookies is a per WebView policy and can be set
differently on different WebView instances.

Apps that target {@link android.os.Build.VERSION_CODES#KITKAT} or below
default to allowing third party cookies. Apps targeting
{@link android.os.Build.VERSION_CODES#LOLLIPOP} or later default to disallowing
third party cookies.

4,http/https混合加载

在现阶段,很多网站都改成了https进行访问,https可以提升访问网站的安全性,防止信息被窃取,如果所有的网页都是https且网页内的链接也是都是https,那就没有混合加载的问题了。但是很多资源现阶段还没有改变成https访问,往往页面都嵌入了http的链接。这种混合网页如果不进行处理,直接加载是会出现错误的。怎么解决这个问题?

  1. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  2. webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
  3. }

 

这也是一个分版本的函数,在api 23之前,默认是可以混合加载的,但是在23时,默认值改成了MIXED_CONTENT_NEVER_ALLOW,因此如果你有混合加载的需求,设置setMixedContentMode为MIXED_CONTENT_ALWAYS_ALLOW。

5,花屏

WebView如果开启了硬件加速,多次打开,会导致界面花屏,或者界面绘制有残留。这个在5.0刚出来的时候发生过多次,但是当时忘记截图了。当时也没有打算要把这些给记录下来。这里我已经复现不了了。如果你遇到了。关闭硬件加速就好了。

6,资源释放

这里我们先截取几张图来看看加载前与加载后webview内存的占用:
加载之前:
这里写图片描述
加载之后:
这里写图片描述
退出页面:
这里写图片描述

可以看到内存蹭蹭蹭的就上去了。就算退出去了也没有减少多少,因此这里必须手动进行资源释放,可以调用如下代码:

  1. @Override
  2. protected void onDestroy() {
  3. super.onDestroy();
  4. if (webView != null) {
  5. root.removeView(webView);
  6. webView.removeAllViews();
  7. webView.destroy();
  8. }
  9. }

 

也可以采用在界面中动态创建webview,之后add到页面中,甚至更有人会为该页面单独设置一个进程,之后退出后会调用exit函数进行资源释放。这里可以根据需求来进行设置。

总结

WebView有这样或者那样的坑。这里只是大致罗列自己遇到的一些,在知乎上也看到一个专门讲WebView坑的查看链接。大家可以去看看,有什么新的发现也可以进行探讨。

(0109)iOS开发之CocoaPods Mac App的安装和使用

这里介绍一个iOS 开发必备好用的工具。Mac CocoaPods App 。不需要每次都再打开终端,一顿操作了。

下载地址: https://cocoapods.org/app

先用xcode创建一个项目

%title插图%num
点击Cocoapods APP 菜单栏里面的Files->New Podfile from Xcode Project ->选择刚刚创建的项目CocoapodsTest.xcodeproj ->Open

%title插图%num
弹出如下界面并输入

target ‘CocoapodsTest’ do
pod ‘AFNetworking’, ‘~> 3.0’
end

注意:此处一定要写上target ‘xxx’ do end 否则是找不到target的

%title插图%num
点击右上角 install

%title插图%num

打开项目验证一下

%title插图%num

OK 就是这么简单, 全程无命令,而且第二次使用的时候会自动填写下面的代码的,方便快捷

target ‘xxxxxx’ do
end

(0099)iOS开发之Xcode编译工程报错问题汇总

1…/Target Support Files/Pods-SNFaceDetectDemo/Pods-SNFaceDetectDemo-frameworks.sh: No such file or directory
完整错误日志

PhaseScriptExecution [CP]\ Embed\ Pods\ Frameworks /Users/suning/Library/Developer/Xcode/DerivedData/SNFaceDetectDemo-gisszeqtrkcmfccrbtanfqzzxrnn/Build/Intermediates.noindex/SNFaceDetectDemo.build/Debug-iphoneos/SNFaceDetectDemo.build/Script-79F5D4BA6B990E748B56D8B9.sh
cd /Users/suning/Desktop/SuningCodeLibs/Face_Detect/静默离线人脸检测/SNFaceDetectDemo
/bin/sh -c /Users/suning/Library/Developer/Xcode/DerivedData/SNFaceDetectDemo-gisszeqtrkcmfccrbtanfqzzxrnn/Build/Intermediates.noindex/SNFaceDetectDemo.build/Debug-iphoneos/SNFaceDetectDemo.build/Script-79F5D4BA6B990E748B56D8B9.sh

/Users/suning/Library/Developer/Xcode/DerivedData/SNFaceDetectDemo-gisszeqtrkcmfccrbtanfqzzxrnn/Build/Intermediates.noindex/SNFaceDetectDemo.build/Debug-iphoneos/SNFaceDetectDemo.build/Script-79F5D4BA6B990E748B56D8B9.sh: line 2: /Users/suning/Desktop/SuningCodeLibs/Face_Detect/静默离线人脸检测/SNFaceDetectDemo/Pods/Target Support Files/Pods-SNFaceDetectDemo/Pods-SNFaceDetectDemo-frameworks.sh: No such file or directory

问题原因:可能是CocoaPods版本不同造成的问题

较高版本的CocoaPods更新工程的时会生成一个文件名为Pods-framework.sh脚本文件,并向工程的.xcodeproj当中添加相关调用脚本的配置,我使用的版本(0.39.0)却不会。当我更新工程时,CocoaPods(0.39.0)只移除了高版本生成的脚本文件,却没有修改配置,导致编译时寻找该文件却找不到,提示错误

根本解决方法:貌似现在*新的就是1.7.4,趁早升级了吧,看到这种错误简直让人心醉啊,升级重新生成工程文件就好了
不想升级也行,告诉你个简单粗暴的方法:直接删除这个文件,再编译就可以了。

%title插图%num

2.iOS 真机测试报如下错误:process launch failed: failed to get the task for process XXXX
解决办法如下图:

%title插图%num

(015)java后台ios开发之web项目中如何添加jar包和删除jar包

注意:jar的导入导出,*不是添加或者删除就成功,一定要记得buildpath的修改.

1.在(014)java后台开发…中我们知道了如何创建一个基本的java web工程。
2.创建一个JsonTest工程,在此工程中导入gson-2.2.4.jar和servlet-api.jar这两个包(gson:用于将持久化对象解析为Json,或将Json序列化为对象。,servlet:提供HttpServletRequest服务的API)。
%title插图%num

由于:我已经把jar包导入成功了,就反向演示删除jar包!
a:先移除jar包的 Build Path。我们把gson-2.2.4.jar 和 gson-2.2.4-source.jar都移除。如图:

%title插图%num
b:查看Build Path是否成功移除。

%title插图%num
点击进入查看:

%title插图%num
c:移除我们导入的jar包。选中后右键,delete就完成了jar包的删除。

%title插图%num
接下来演示引用jar包
a:删除时只剩下servlet-api-jar包。接着导入gson-2.2.4.jar。

%title插图%num
b:copy gson-2.2.4.jar和gson-2.2.4-source.jar(这个是前者的源码包)这两个包。这里我添加两个包,是为了能后面查看.class的源码。在下图WebContent的目录lib下粘贴,目录不要错了!

%title插图%num

c:add Build Path

%title插图%num

%title插图%num

添加就算成功了!你会发现.class看不到没有源码:

%title插图%num
怎么查看尼:点击 “Attach Source code” 按钮。

%title插图%num
ok 后奇迹出现了。.class 文件出现java源码了。yes!

(0015)iOS 开发之Mac上安装MySQL服务与创建数据库的基本步骤

1.安装MySQL (免费)

官网现下载地址 http://dev.mysql.com/downloads/mysql/   (我选的mysql-5.7.17-macos10.12-x86_64.dmg)

点击download 会跳转到另外一个界面,这个界面是提示你需不需要注册的,直接选择*下面的“No thanks,just take me to downloads!”即开始下载。

2.解压后分别安装 mysql-5.7.17-osx10.6-x86_64.pkg:这个是MySql的主要程序包;

安装成功后会弹出一个弹框,记得截图,因为有个初始化密码在里面!

%title插图%num

3.安装成功后,系统偏好设置里,会出现如下图标:

%title插图%num

4. 双击MySQL,图标进到下面页面:这里可以start/stop MySQL Server 。

%title插图%num
安装完成了。

终端启动mysql 的方法这里两种:

方法1:

$:alias mysql=/usr/local/mysql/bin/mysql

$:cd /usr/local/mysql/bin/

(上面两步可并作一步$:/usr/local/MySQL/bin/mysql -u root -p)

$:  mysql -u root -p

$:  Enter password: 输入截图上的密码:

方法2:

1.PATH=$PATH:/usr/local/mysql/bin/
2.mysql -u -p

3.Enter password: 输入截图上的密码:

直接输入就行了,此时如果你没有改密码,直接敲回车。这样就可以访问你的数据库服务器了。

如遇到:

mysql: [ERROR] Found option without preceding group in config file /etc/my.cnf at line 1!

mysql: [ERROR] Fatal error in defaults handling. Program aborted!

解决办法:./etc/my.cnf  找到这个文件,把里面的内容删除,删除不了,直接拉到桌面,然后编辑把内容清空,然后把/etc/里的 my.cnf删除,把桌面的my.cnf copy到/etc/。

ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement.

操作提示:You must reset your password using ALTER USER statement before executing this statement.
按照提示ALTER USER 修改密码无效,后来发现执行如下命令即可:
mysql> SET PASSWORD = PASSWORD(‘123456’);   //123456 是重置的新密码

接下来可以访问服务器的数据库的一些基本操作了

(http://www.cnblogs.com/qq1871707128/p/6041704.html)

(1)显示所有数据库列表:show databases;

(2)建库:create database Mytest;(Mytest库名)

(3)打开某个数据库(比如数据库:Mytest):use Mytest;

(4)显示本库中的所有表:show tables;

(5)建表:create table 表名 (字段设定列表); // :creat table mytest_acount (col1 INT, col2 CHAR(5), col3 DATE);  表至少一列。

(6)显示某表的结构:describe table1;

(7)删库:drop database 库名;

(8)删表:drop table 表名;

(9)将表中的记录清空:delete from 表名;

(10)显示表中的记录:select  *  from 表名;

6、退出mysql:exit

7、启动和停止mysql

启动:/usr/local/mysql/share/mysql.server start

停止:/usr/local/mysql/bin/mysqladmin -u root -p shutdown

输入root密码。

快捷路径配置方法:

1.  打开终端输入:  pico .bash_profile;    回车

2. 将此路径配置进去:  export PATH=$PATH:/usr/local/mysql/bin/

3. 重新打开终端,然后 直接: mysql -u -p ;

(即可一般都会添加这个路径,否则很不方便使用mysql的说~)

修改MySQL登录密码的方法:

1. $ ./mysql

2.$ FLUSH PRIVILEGES;

3.$ SET PASSWORD FOR ‘root’@’localhost’ = PASSWORD(‘你的新密码’);

(至此,密码修改完成,可以成功登陆。)

mysql验证功能的方法:

1.登录管理员权限$ sudo su

2.禁止mysql验证功能 $ ./mysqld_safe –skip-grant-tables &

3.mysql会自动重启(偏好设置中mysql的状态会变成running)

(注意:设置完成以后就不能手动关闭了,除非开机重启)

 

Mac-使用技巧之快速新建txt文本

*近在使用mac系统进行web网页开发,由于没有使用开发环境,因此出现大量的新建文本文件操作。mac上没有像windows一样右键新建文本文件的功能还是挺别扭的,几经搜索学习,总结以下两种个人认为*为方便的方法,供各位mac同道中人借鉴。


Automator工具介绍

首先说明一下这是macOS系统自带的一款功能强大的程序,不同版本系统该程序所在的位置可能不同,仅以目前较新的macOS Sierra为例:
%title插图%num
该程序就在Finder中应用程序的根目录下:
%title插图%num
其他版本有的在应用程序中的实用工具文件夹下,实在不行可以使用mac自带的Spotlight进行搜索。

大概意思就是这是个可以只要通过点击拖拽鼠标,或者编写脚本等操作就可以将一系列动作组合成一个工作流,从而帮助你自动的(可重复的)完成一些复杂的工作。

新建Finder服务方法

 

1、打开应用程序内的Automator,然后文稿类型选择服务,如下图:
%title插图%num
2、资源库中选择文本,操作中选择新建文本文件,同时,设置“服务”收到为没有输入,如下图:
%title插图%num
3、保存,文件名可以自己设置,这里设置为“新建文本文件”,如下图:
%title插图%num
4、打开Finder时,在*上排单击Finder,选择服务,便可以看到新建文本文件的服务,直接点击就能方便的新建txt文本了。如下图:
%title插图%num

新建Finder快捷应用程序方法

 

1、打开Automator,然后文稿类型选择应用程序,如下图:

%title插图%num
2、依次单击实用工具-运行AppleScript
%title插图%num
3、将如下代码复制进编辑框中,然后保存该工程文件,位置和命名可以自定义,这里将文件保存在应用程序根目录下,命名为zzy_NewFile。代码如下:

  1. on run {input, parameters}
  2. tell application “Finder”
  3. set selection to make new file at (get insertion location)
  4. end tell
  5. return input
  6. end run

4、默认情况下,该应用程序会和Automator一样的图标,为了便于分辨,*好可以修改个程序图标。具体方法为:找到一个logo图标,然后右键复制,在Finder相应位置找到刚才的应用程序,右键显示简介,点击图标,然后command+v复制,则可成功修改图标,如下图所示:
%title插图%num
5、*后一步,将该应用程序添加到Finder的快捷工具栏中。
首先,打开Finder,在工具栏右键,选择自定义工具栏,如下图:
%title插图%num
另外再打开一个Finder,找到刚才的应用程序,将其拖到*上方工具栏,在选择仅显示图标的情况下,会在工具栏中显示刚才应用程序的图标,如下图所示:
%title插图%num
以后在使用Finder时,只要在相应的文件夹下点击工具栏上应用程序的图标,就能方便的建立一个未命名的文本文件了,如图:
%title插图%num

总结

macOS系统提供了诸多如Automator类似的方便且功能强大的系统自带软件,为进一步设计自己的操作留下了巨大的空间,相比于windows虽然刚上手有些不适应,但随着会越用越熟练,终究还是会喜爱上这个系统的。

iOS进阶之架构设计MVVM的实现示例(4)

实践是检验真理的唯一真理。让我们来看个简单的实现MVVM设计的demo例子吧。

MVVM加深理解
MVVM模式将Presenter改名为ViewModel,基本上与MVP模式完全一致。

唯一的区别是,它采用双向绑定(data-binding) : View<->ViewModel, ViewModel作为Model中值的映射,是数据发生改变时,通知View中发生改变,以后不需要考虑View和Model之间的交互更新,只需着手界面布局逻辑即可。

①View和Model 不直接关联,而是通过ViewModel作为枢纽,沟通View和Model之间的关系。

②View中控件的值与ViewModel属性进行绑定,通过KVO键值观察(这样当model的值发生变化时,View会自动发生改变)

View和Model通过ViewModel实现动态关联。

MVVM demo
MVVModel代码
#import <Foundation/Foundation.h>

@interface MVVMModel : NSObject

@property(nonatomic,copy)NSString *name;

@end

MVVMViewModel代码
MVVMViewModel.h
#import <Foundation/Foundation.h>
#import “MVVMModel.h”
@interface MVVMViewModel : NSObject
@property(nonatomic,copy)NSString *nameStr;
@property(nonatomic,strong)MVVMModel *model;

-(void)setWithModel:(MVVMModel *)model;
-(void)clickChangeName;
@end

MVVMViewModel.m

#import “MVVMViewModel.h”

@implementation MVVMViewModel
-(instancetype)init{
if (self = [super init]) {

}
return self;
}

-(void)setWithModel:(MVVMModel *)model{
self.model = model;
self.nameStr = model.name;
}
-(void)clickChangeName{
self.model.name = [NSString stringWithFormat:@”name%d”,arc4random()%10];
self.nameStr = self.model.name;
NSLog(@”%@”,self.nameStr);
}
@end

MVVMView代码
#import <UIKit/UIKit.h>
#import “MVVMViewModel.h”
@interface MVVMView : UIView
-(void)setWithViewModel:(MVVMViewModel *)vm;
@end

#import “MVVMView.h”
@interface MVVMView ()

@property(nonatomic,strong)MVVMViewModel *vm;
@property(nonatomic,strong)UILabel *label;
@property(nonatomic,strong)UIButton *button;

@end
@implementation MVVMView

– (instancetype)init
{
self = [super init];
if (self) {
self.backgroundColor = [UIColor whiteColor];
self.frame = [UIScreen mainScreen].bounds;

self.label = [[UILabel alloc]initWithFrame:CGRectMake(150,100 , 100, 30)];
self.label.backgroundColor = [UIColor orangeColor];
[self addSubview:_label];

self.button = [UIButton new];
_button.backgroundColor = [UIColor redColor];
[_button setTitle:@”点击” forState:UIControlStateNormal];
[_button addTarget:self action:@selector(mvvmClickChangModel) forControlEvents:UIControlEventTouchUpInside];
_button.frame = CGRectMake(150, 200, 50, 50);
[self addSubview:_button];
}
return self;
}
– (void)setWithViewModel:(MVVMViewModel *)vm {

self.vm = vm;
//KVO
[self.vm addObserver:self forKeyPath:@”nameStr” options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
self.label.text = vm.nameStr;

}

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change: (NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
if ([keyPath isEqualToString:@”nameStr”]&&[change objectForKey:NSKeyValueChangeNewKey]) {
NSNumber *new = [change objectForKey:NSKeyValueChangeNewKey];
self.label.text = [NSString stringWithFormat:@”%@”,new];
}
}
-(void)mvvmClickChangModel{
[self.vm clickChangeName];
}

-(void)dealloc{
[self.vm removeObserver:self forKeyPath:@”nameStr”];
}
@end

ViewController.m
#import “ViewController.h”
#import “MVVMView.h”
#import “MVVMModel.h”
#import “MVVMViewModel.h”

@interface ViewController ()
@property (nonatomic, strong) MVVMViewModel * viewModel ;
@end

@implementation ViewController
– (void)viewDidLoad {
[super viewDidLoad];
MVVMView *MView = [MVVMView new];
MVVMModel *model = [MVVMModel new];
model.name = @”name1″;
MVVMViewModel *viewModel = [MVVMViewModel new];
[self.view addSubview:MView];
//*viewModel 作为枢纽 沟通view和model之间关系
[viewModel setWithModel:model];
[MView setWithViewModel:viewModel];
}
@end

这个demo 有着很典型的MVP的影子。同时增加了View和ViewModel 双向绑定的部分。例子很简单,容易理解。

iOS开发:MVVM的使用分析

在iOS开发过程中,MVC的使用可谓是众所周知,作为iOS开发人员也都经常使用这个模式。在MVC下,所有的对象都被归类成一个Model、一个View、一个Controller。虽然现在MVC仍然是主流的框架,但是它也被慢慢的替换成MVVM,因为越来越多的开发人员调侃MVC为Massive View Controller。

一、MVVM

MVVM是Model-View-ViewModel的简写。微软的WPF带来了新的技术体验,如Silverlight、音频、视频、3D、动画…..,这导致了软件UI层更加细节化、可定制化。同时,在技术层面,WPF也带来了诸如Binding、Dependency Property、Routed Events、Command、DataTemplate、ControlTemplate等新特性。MVVM(Model-View-ViewModel)框架的由来便是MVP(Model-View-Presenter)模式与WPF结合的应用方式时发展演变过来的一种新型架构框架。它立足于原有MVP框架并且把WPF的新特性糅合进去,以应对客户日益复杂的需求变化。

%title插图%num
一种可以很好地解决Massive View Controller问题的办法就是将Controller 中的展示逻辑抽取出来,放置到一个专门的地方,而这个地方就是ViewModel 。MVVM衍生于MVC,是对 MVC 的一种演进,它促进了UI代码与业务逻辑的分离,而且正式规范了视图和控制器紧耦合的性质,并引入新的组件。
在MVVM中,View和View Controller是联系在一起,可以把它们视为一个组件,View和View Controller都不能直接引用Model,而是通过引用视图模型(ViewModel),ViewModel是一个放置用户输入验证逻辑、视图显示逻辑、发起网络请求和其他代码的地方,使用MVVM会轻微的增加代码量,但总体上减少了代码的复杂性、耦合性。
二、MVVM设计模式模块

由于WPF技术出现,使得MVP设计模式有所改进,MVVM模式便是使用的是数据绑定基础架构。它们可以轻松构建UI的必要元素。可以参考The Composite Application Guidance for WPF(prism),View绑定到ViewModel,然后执行一些命令再向它请求一个动作。然而又反过来,ViewModel再跟Model通讯,告诉它更新来响应UI。这样便为应用构建UI非常的简单容易。给一个应用程序上贴一个界面越容易,外观设计师就越容易使用Blend来创建一个漂亮的界面。同时,当UI和功能越来越低耦合的时候,功能的可测试性就越来越强。
在MVP模式中,为了让UI层能够从逻辑层上分离下来,设计师们在UI层与逻辑层之间加了一层interface。无论是UI开发人员还是数据开发人员,都要尊重这个契约,按照它进行设计和开发。这样,理想状态下,无论是Web UI还是Window UI就都可以使用同一套数据逻辑了。借鉴MVP的IView层养成习惯,View Model听起来比Presenter要贴切得多;会把一些跟事件、命令相关的东西放在MVC的’C’,或者是MVVM的’VM’。
三、MVVM的优缺点

(一)MVVM模式和MVC模式一样,主要目的是分离视图(View)和模型(Model),有几大优点:
1. 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的”View”上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
2. 可重用性。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计,使用Expression Blend可以很容易设计界面并生成xml代码。
4. 可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。

%title插图%num
(二)MVVM模式的缺点:
1.数据绑定使得Bug很难被调试。如果界面发生错误,有可能是View的代码有问题,也有可能是Model的代码有问题。数据绑定使得一个位置的Bug被快速传递到别的位置,要想定位原始出问题的地方,就变得不那么容易了。
2.对于大型项目,数据绑定和数据转化需要花费更多的内存(成本)。主要在于:数组内容的转化成本较高:数组里面每项都要转化成Item对象,如果Item对象中还有类似数组,就很头疼。转化之后的数据在大部分情况是不能直接被展示的,为了能够被展示,还需二次转化。只有在API返回的数据高度标准化时,这些对象原型(Item)的可复用程度才高,否则容易出现类型膨大,提高维护成本。
3.调试时通过对象原型查看数据内容,不如直接通过NSDictionary或者NSArray这样直观。
4.同一API的数据被不同View展示时,难以控制数据转化的代码,它们有可能会散落在任何需要的地方。

%title插图%num

四、MVVM模式的其他内容

1.使用MVVM来开发控件。由于控件在大部分情况下不涉及到数据的持久化,所以如果将M纯粹理解为DomainModel的话,使用MVVM模式来进行自定义控件开发,实际上可以省略掉M,变成了VVM。

2.View可以引用ViewModel,但反过来不行(即:不要在ViewModel中引入#import UIKit.h,任何视图本身的引用都不能放在ViewModel中),ViewModel可以引用Model,但反过来不行。
3.MVVM可以兼容当前你使用的MVC架构,也可以增加你开发的应用的可测试性;MVVM配合一个绑定机制效果*好(eg:ReactiveCocoa)。
4.ViewController尽量不要涉及业务逻辑,让ViewModel去做业务逻辑;ViewController 只是一个“中间人”,接收View的事件、调用ViewModel的方法、响应ViewModel的变化;ViewModel *不能包含视图View(UIKit.h),否则就跟View产生耦合,不方便复用和测试。
5.ViewModel之间可以有依赖;ViewModel要避免过于臃肿冗余,否则讲重蹈Controller的覆辙,变得难以维护。

%title插图%num

五、MVVM模式总则

MVC的设计模式也并非是必须摒弃的架构,*低目前MVC设计模式仍是iOS开发的主流框架。MVVM是MVC的升级版,完全兼容当前的MVC架构,MVVM虽然促进了UI 代码与业务逻辑的分离,一定程度上减轻了ViewController的耦合度,但是View和ViewModel之间的数据绑定,使得MVVM变得复杂和难用了,如果不能处理和驾驭二者间的数据绑定,仍然会造成Controller代码过于复杂冗余、代码逻辑不易维护的问题。
一个轻量级的ViewController是基于MVC和MVVM模式进行代码职责的分离而打造的。MVC和MVVM优缺点共存,但是优点产生的效果远远盖住了缺点的不良影响,它们的低耦合性、封装性、可测试性、可维护性和多人协作便利等优势大大提高了开发效率。只要处理协调好二者之间的矛盾,就能很好的驾驭它们。

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速