I want to set up a project in Android Studio. However, I don't want an Android App, just the test project.
Following the lastest release of UiAutomator, I was trying to set a class extending ActivityInstrumentationTestCase2 and start my tests from there.
However, I've stumbled in one thing: I can't figure out how to create the project without making it an app.
The options for creating a new project are:
Start a new Android Studio Project
Open existing projects
Import projects
I did:
Start a new project, give it a name, set minSDK and choose "No activity"
Open build.gradle (under app) and add the dependencies and instrumentation information mentioned in the end of Testing Support Library
Opened androidTest under src and changed the main file: changed to ActivityInstrumentationTestCase2, added setUp and tearDown; defined RunWith Junit4 (as indicated in Testing Support Library)
I build the project (build successful) - Press the green arrow next to build in the "action bar"
My problems are:
How do I install this in the device?
How do I run it in the device?
Do I need to do anything in the AndroidManifest?
Am I editing in the right place? Should I do anything under src/main?
I'd appreciate that the install and run instructions would be both for how to do it through Android Studio and using the command line (if you only know one of them post it anyway please).
Note: this is the first time I'm using Android Studio
Thanks in advance.
EDIT:
Right now I can build and run but it tells me I have no tests to run (empty test suite). Here are my graddle and my code.
My build.graddle is as follows:
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "21.1.2"
defaultConfig {
applicationId "androidexp.com.ceninhas"
minSdkVersion 21
targetSdkVersion 22
versionCode 1
versionName "1.0"
testInstrumentationRunner="android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
packagingOptions {
exclude 'LICENSE.txt'
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile 'com.android.support.test:testing-support-lib:0.1'
androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.0.0'
}
My source code (under src/androidTest/java/package) is:
#RunWith(AndroidJUnit4.class)
public class ApplicationTest extends ActivityInstrumentationTestCase2<Activity> {
public final static String ACTIVITY_NAME = "com.calculator.Main";
public final static Class<?> autActivityClass;
static {
try {
autActivityClass = Class.forName(ACTIVITY_NAME);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
public ApplicationTest(){
super((Class<Activity>)autActivityClass);
}
#Before
public void setUp() throws Exception{
super.setUp();
injectInstrumentation(InstrumentationRegistry.getInstrumentation());
}
#After
public void tearDown() throws Exception{
super.tearDown();
}
#Test
public void cenas(){
assertTrue(true);
}
}
The run log on the console was:
Testing started at 18:06 ...
Waiting for device.
Target device: lge-nexus_5-08e506c10ddef123
Uploading file
local path: C:\Users\Ines\workspace\Ceninhas\app\build\outputs\apk\app-debug.apk
remote path: /data/local/tmp/androidexp.com.ceninhas
No apk changes detected. Skipping file upload, force stopping package instead.
DEVICE SHELL COMMAND: am force-stop androidexp.com.ceninhas
Uploading file
local path: C:\Users\Ines\workspace\Ceninhas\app\build\outputs\apk\app-debug-androidTest-unaligned.apk
remote path: /data/local/tmp/androidexp.com.ceninhas.test
No apk changes detected. Skipping file upload, force stopping package instead.
DEVICE SHELL COMMAND: am force-stop androidexp.com.ceninhas.test
Running tests
Test running startedFinish
Empty test suite.
What am I doing wrong?
I am also using uiautomator 2.0 from AndroidStudio. Here are some answers to your questions.
How do I install this in the device?
How do I run it in the device?
Make sure your device is connected using
adb devices
if not, you must connect it using
adb kill-server
adb connect xxx.xxx.xxx.xxx
Then from AndroidStudio, right click on your test class and click on "Run YourTestCase".
Do I need to do anything in the AndroidManifest?
I have nothing special in my manifest, but be sure you add
android {
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}
in your build.gradle
Am I editing in the right place? Should I do anything under src/main?
Yes, you are editing at the right place. But you can move your code to src/main. To do so, you will need to change androidTestCompile to compile in your build.gradle file.
I did not try to run test from command line yet, but you can see AndroidStudio commands, maybe it can help.
I hope it helped you.
EDIT 1
I use this code
build.gradle (projectRoot)
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22.0.0"
lintOptions {
abortOnError false
}
packagingOptions {
exclude 'NOTICE'
exclude 'LICENSE.txt'
}
defaultConfig {
minSdkVersion 19
targetSdkVersion 22
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.0.0'
compile 'com.android.support.test:testing-support-lib:0.1'
compile 'com.android.support.test.uiautomator:uiautomator-v18:2.0.0'
compile project(':aiccore')
}
LoginTestCase (projectRoot/src/main/LoginTestCase.java)
public class LoginTestCase extends InstrumentationTestCase {
protected UiDevice device = null;
protected String appName;
public LoginTestCase() {
this("YourAppName")
}
public LoginTestCase(String appName) {
this.appName = appName;
}
public void runApp(String appName) throws UiObjectNotFoundException, RemoteException {
device = UiDevice.getInstance(getInstrumentation());
device.pressHome();
device.waitForWindowUpdate("", 2000);
UiObject2 allAppsButton = device.findObject(By.desc("Apps"));
allAppsButton.click();
device.waitForWindowUpdate("", 2000);
UiScrollable appViews = new UiScrollable(new UiSelector().scrollable(true));
appViews.setAsHorizontalList();
UiObject settingsApp = appViews.getChildByText(new UiSelector().className(TextView.class.getName()), appName);
settingsApp.clickAndWaitForNewWindow();
assertTrue("Unable to detect app", settingsApp != null);
}
#Override
public void setUp() throws RemoteException, UiObjectNotFoundException {
this.runApp(appName);
}
#Override
public void tearDown() throws RemoteException, UiObjectNotFoundException {
//Empty for the moment
}
public void testUS1() {
UiObject2 usernameLabel = device.findObject(By.clazz(TextView.class.getName()).text("Username"));
assertTrue("Username label not found", usernameLabel != null);
}
Well, actually, you should not write test code that way. Just keep your code under the src/androidTest folder, and write test code like this:
#RunWith(AndroidJUnit4.class)
#SdkSuppress(minSdkVersion = 18)
public class ChangeTextBehaviorTest {
private static final String BASIC_SAMPLE_PACKAGE
= "com.example.android.testing.uiautomator.BasicSample";
private static final int LAUNCH_TIMEOUT = 5000;
private static final String STRING_TO_BE_TYPED = "UiAutomator";
private UiDevice mDevice;
#Before
public void startMainActivityFromHomeScreen() {
// Initialize UiDevice instance
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
// Start from the home screen
mDevice.pressHome();
// Wait for launcher
final String launcherPackage = mDevice.getLauncherPackageName();
assertThat(launcherPackage, notNullValue());
mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)),
LAUNCH_TIMEOUT);
// Launch the app
Context context = InstrumentationRegistry.getContext();
final Intent intent = context.getPackageManager()
.getLaunchIntentForPackage(BASIC_SAMPLE_PACKAGE);
// Clear out any previous instances
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(intent);
// Wait for the app to appear
mDevice.wait(Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)),
LAUNCH_TIMEOUT);
}
#Test
public void checkPreconditions() {
assertThat(mDevice, notNullValue());
}
#Test
public void testChangeText_sameActivity() {
// Type text and then press the button.
mDevice.findObject(By.res(BASIC_SAMPLE_PACKAGE, "editTextUserInput"))
.setText(STRING_TO_BE_TYPED);
mDevice.findObject(By.res(BASIC_SAMPLE_PACKAGE, "changeTextBt"))
.click();
// Verify the test is displayed in the Ui
UiObject2 changedText = mDevice
.wait(Until.findObject(By.res(BASIC_SAMPLE_PACKAGE, "textToBeChanged")),
500 /* wait 500ms */);
assertThat(changedText.getText(), is(equalTo(STRING_TO_BE_TYPED)));
}
}
For detail please look out: UIAutomator Test sample
Related
I have a simple app with 2 activities: MainActivity (with 2 Fragments) and GameActivity.
In one of the Fragments "FragmentHome" I have a FloatingButton which upon press launches my GameActivity.
When the game ends I show an AlertDialog with a button which upon click does: GameActivity.this.onBackPressed(); which returns the user back to FragmentHome where he can launch the game again.
The problematic scenario is: the user launches the game (from the FragmentHome FloatingButton), plays it, the game is over in few seconds and user is directed back to FragmentHome where he launches the game again; if to repeat this 7-8 times then at some point the FloatingButton click will just terminate the application without a logcat error thrown. The only weird 'hint' in logcat I can see is D/StrictMode: StrictMode policy violation: android.os.strictmode.DiskReadViolation which does not really say much about the app termination.
I monitored the App memory usage (from the Android device itself): it is normal, between 53MB and 66MB.
My FloatingButton on click:
myView.findViewById(R.id.btnStartGame).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (!AppController.appOnline) {
Snackbar.make(lstLuckyUsers, getString(R.string.txt_no_internet), 2000).show();
return;
}
Intent gameIntent = new Intent(FragmentHome.this.context, GameActivity.class);
FragmentHome.this.context.startActivity(gameIntent);
}
});
My GameActivity AlertDialog button click:
MaterialButton btnOK = dialogView.findViewById(R.id.btnOK);
if (btnOK != null)
btnOK.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// close dialog and game Activity
gameOverDialog.dismiss();
GameActivity.this.onBackPressed();
}
});
Could you please advise where shall I look for the problem? It feels like the Garbage Collector is not doing its job as expected or there is a Framework I use and this brings the troubles.
The dependencies are plain standard:
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
defaultConfig {
applicationId "com.mygame.mygame"
minSdkVersion 23
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.navigation:navigation-fragment:2.3.3'
implementation 'androidx.navigation:navigation-ui:2.3.3'
// volley lib
implementation 'com.android.volley:volley:1.1.1'
// FAN Ad
implementation 'com.facebook.android:audience-network-sdk:6.0.0'
// Google In-App Review
implementation 'com.google.android.play:core:1.9.1'
}
The application runs but when i try to use the camera only a disturbed grey screen appears and the logs tab gives me two errors:
E/libc: Access denied finding property "vendor.camera.aux.packagelist"
Access denied finding property "vendor.camera.aux.packagelist2"
AndroidManifest.xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
build.gradle (Module:app)
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 22
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
}
build.gradle(Project:camera)
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.4.0'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
First, check your Android version. If it is running on Android 6.0 and higher (API level 23+), then you need to :
Declare a permission in the app manifest. Make sure to insert the permission above the application tag.
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<application ...>
...
</application>
Then, request that the user approve each permission at runtime
if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) !=
PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.CAMERA},
50); }
I had this in my android manifest that prevented the app from using the camera.
android:hardwareAccelerated="false"
Removing it helped me.
Firstly, to check if it is a permission problem, go to the application manager of your phone, find your programme's name, and check the permission list. See if it is allowed to use the camera.
Secondly, open the XML of the layout where your "SurfaceView" resides, check if the "SurfaceView" node has the property "android:background". You must remove it if it is there. No idea why this is never mentioned.
<SurfaceView
android:layout_width="0dp"
android:layout_height="300dp" android:id="#+id/cameraPreview"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" android:background="#3F51B5"/>
Pay attention to the "android:background" in the above snippet. Remove it if you see it.
Thirdly, make sure you start the camera in the "surfaceCreated" event handler rather than anywhere else. You can't directly start the camera in "onViewCreated"!
Sample code just in case:
public void onViewCreated(#NonNull final View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
final CameraSource cameraSource = new CameraSource.Builder(getContext(), barcodeDetector)
.setRequestedPreviewSize(640, 480)
.setRequestedFps(10)
.setAutoFocusEnabled(true).build();
((SurfaceView) view.findViewById(R.id.cameraPreview)).getHolder().addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
if(ContextCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.CAMERA}, 1);
}
try {
cameraSource.start(holder);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
cameraSource.stop();
}
});
}
Reason: FileNotFoundException open failed: XXXXXXX EPERM (Operation not permitted)
Adding all the permissions still reports an error, mainly because the permissions requirements of the Android system are very strict.
It is recommended to change to:
ContextWrapper cw = new ContextWrapper(getApplicationContext());
File directory = cw.getExternalFilesDir(Environment.DIRECTORY_MUSIC);
File file = new File(directory, "something" + ".MP3");
public static String DIRECTORY_MUSIC = "Music";
public static String DIRECTORY_ALARMS = "Alarms";
public static String DIRECTORY_NOTIFICATIONS = "Notifications";
public static String DIRECTORY_PICTURES = "Pictures";
public static String DIRECTORY_MOVIES = "Movies";
public static String DIRECTORY_DOWNLOADS = "Download";
public static String DIRECTORY_DCIM = "DCIM";
public static String DIRECTORY_DOCUMENTS = "Documents";
If you are using a firebase plugin in your project, remove it. I was using the firebase crashlytics that camera prevents opening.
I am also seeing same error in my console when I am trying to open camera. However, it does not cause any other side effects.
I think it's best to ignore this error unless it stops app from running. or limits any functionality.
Found a post here that claims to resolve the issue by adding
<application>
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
</application>
However, this did not work in my case. Testing on Mi Note 7
I too faced same error while implementing video capture option, this in my case due to missing Storage and Camera permissions.
Resolved using below code
if ((checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) || (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)) {
Log.d(TAG, "No camera and storage permission");
requestPermissions(new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, MY_PERMISSIONS_REQUEST_CODE);
}
In my case, you must be following an android jetpack for a camera course in here [https://developer.android.com/codelabs/camerax-getting-started#0][1]
After that, change the companion object like this.
companion object {
private const val TAG = "CameraXBasic"
private const val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS"
private const val REQUEST_CODE_PERMISSIONS = 10
private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA)
}
Don't forget to add permission to access storage in the manifest.xml
In my case, the cause of the problem is that the surfaceTexture is available before the ui is not ready. To solve it, i add some delay (i don't know whether it is the correct solution or not, but it's working).
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int arg1, int arg2) {
mSurfaceTexture = surfaceTexture;
try {
Thread.sleep(200); // add some delay
} catch (InterruptedException e) {
e.printStackTrace();
}
cameraHelper.openCamera(mSurfaceTexture);
}
I create app named "onlineContact" that save your contact in cloud and you can access your contacts from any Android phone using login. This app is working fine when I test it. I connect my mobile with computer and run it from android studio it's good. So, I upload this app on Google Play Store but When I download and install app from play store it's not worked. What's the problem with this.
Here is my app link on Play store onlineContact
Install it click on any link like login, create account or forget password.
When you clicked any of them it's crashed.
I tried it again from android studio and install it and it's working fine. Now how can I checked what the problem with this. I'm not able to place all code of my app here. Please help me about this what i can do.
UPDATE
Bulid.gradil(Modual:app)
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "com.azeem.onlinecontact"
minSdkVersion 11
targetSdkVersion 24
versionCode 3
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:24.2.1'
testCompile 'junit:junit:4.12'
compile 'com.google.android.gms:play-services-ads:10.0.1'
}
LoginActivity
public class LoginActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
}
protected void login(View view){
EditText etxtPhoneNumber = (EditText) findViewById(R.id.etxtPhoneNumber);
EditText etxtPassword = (EditText) findViewById(R.id.etxtPassword);
String phoneNumber = etxtPhoneNumber.getText().toString();
String password = etxtPassword.getText().toString();
String action = "login";
BackgroundWorker backgroundWorker = new BackgroundWorker(this);
backgroundWorker.execute(action, phoneNumber, password);
}
protected void forgetPassActivity(View view){
startActivity(new Intent(this, ForgetPassword.class));
}
protected void createAccount(View view){
Intent loginIntent = new Intent(this, CreateAccount.class);
startActivity(loginIntent);
}
#Override
public void onBackPressed() {
moveTaskToBack(true);
}
}
This activity open fine when I run app But it not work any further when I clicked on any button it will crashed.
One of my application suddenly fails on startup, with the following error message :
java.lang.VerifyError: Rejecting class
com.sample.BufferManagerImpl because it failed
compile-time verification (declaration of
'com.sample.BufferManagerImpl' appears in
/data/app/com.sample.myapp-1/base.apk)
It only fails on devices using the ART virtual machine, but not on Dalvik
The issue is due to having a synchronized block inside a try-catch block, for example :
try {
synchronized (mLock) {
updateState();
}
} catch (IllegalStateException e) {
}
Apparently this is not a good practice, but as soon as I change it like this it works :
synchronized(mLock) {
try {
updateState();
} catch (IllegalStateException e) {
}
}
in android studio 2.1 ,the instant run will cause this problem,just run after close the instant run function.
File -> Preferences > Build Execution -> Deployment -> Instant Run
Disable the first checkbox:
Enable Instant Run to hot swap.....
If you are building with Jack, make sure it's turned off from build.gradle
defaultConfig {
...
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
jackOptions {
enabled false
}
}
This error also might happen due to usage of Mockito within an Android InstrumentationTest. If the error appears on mocking objects then you have to add this lines to you gradle-file:
androidTestCompile "com.crittercism.dexmaker:dexmaker:1.4"
androidTestCompile "com.crittercism.dexmaker:dexmaker-dx:1.4"
androidTestCompile "com.crittercism.dexmaker:dexmaker-mockito:1.4"
This works for me with Mockito 1.10.15 and 1.10.19.
i had this problem to with android 5. my app did correctly on 4 or below but on android 5 devices i had crash.
i broke my codes with multiple Threads and it fixed.
if your code wants to change the UI use handler .
Thread Thread = new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
handler.post(new Runnable() {
#Override
public void run() {
use this if your codes will change the Ui
.
.
.
.
.
What is the best way to remove logging from production android application. It appears that proguard does not do this completely, strings still get written, so I was wondering what is best solution to remove the logging from production code?
The IMO best solution is to write code like below wherever you call your logging methods.
if (SOME_LOG_CONSTANT) Log.d(TAG, "event:" + someSlowToEvaluateMethod());
By changing 1 constant you strip away every logging that you don't want. That way the part behind if (false) should not even get into your .class file since the compiler can completely remove it (it is unreachable code).
This also means that you can exclude whole code blocks from your release software if you wrap them in that if. That's something even proguard can't do.
the SOME_LOG_CONSTANT can be BuildConfig.DEBUG if you use SDK Tools r17 and above. That constant is automatically changed for you depending on build type. Thx #Christopher
Use Timber, it's a nice library to configure your logs:
https://github.com/JakeWharton/timber
Example of use: Timber.d("Activity Created");
Example of configuration:
public class ExampleApp extends Application {
#Override public void onCreate() {
super.onCreate();
if (BuildConfig.DEBUG) {
Timber.plant(new DebugTree());
} else {
Timber.plant(new CrashReportingTree());
}
}
/** A tree which logs important information for crash reporting. */
private static class CrashReportingTree extends Timber.Tree {
#Override protected void log(int priority, String tag, String message, Throwable t) {
if (priority == Log.VERBOSE || priority == Log.DEBUG) {
return;
}
FakeCrashLibrary.log(priority, tag, message);
if (t != null) {
if (priority == Log.ERROR) {
FakeCrashLibrary.logError(t);
} else if (priority == Log.WARN) {
FakeCrashLibrary.logWarning(t);
}
}
}
}
Configuring Your Application for Release tells you to simply remove Log commands before releasing your application.
You can deactivate logging by removing calls to Log methods in your source files.
What I do is to create a proprietary static log method that reads a global boolean of something like this:
class MyLog {
private static final boolean LOGGING = true; //false to disable logging
public static void d(String tag, String message) {
if (LOGGING) {
Log.d(tag, message);
}
}
/* ... same for v, e, w, i */
}
Use this everywhere you want to log.
MyLog.d("Tag", "This will only work with LOGGING true");
The best way is to handle this in from my experience,
firstly while logging check for BuildConfig.DEBUG flag to print log.
Class LogUtils{
public static void log(Class clazz, String message) {
if (BuildConfig.DEBUG) {//log only in debug mode
Log.d(clazz.getSimpleName(), message);
}
}
}
then enable minify to your app module gradle file, this will enable proguard to optimize your binary
android {
buildTypes {
release {
minifyEnabled true
//shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
}
this will keep debug logs in your debug builds.
But in your release builds the if (BuildConfig.DEBUG) condition is never true, hence proguard will strip off the unreachable log code from your binary.
I suggest you to see this Log extension class.
It enables you to have a fine control on logs.
For example, you can disable all logs (thanks to a configuration file) or just the logs of some packages or classes.
Moreover, it adds some useful functionalities (for instance you don't have to pass a tag for each log).
Simply add this method to your code and use Logd instead of Log.d
private static final String TAG = "your own tag to recognize log entries from this app";
public static void Logd(String txt) {
if (BuildConfig.DEBUG&&BuildConfig.BUILD_TYPE.equals("debug")) Log.d(TAG,txt);
}
I know that the string will still be evaluated, but we are talking microseconds in extra processing time. Noone will ever notice a difference in app speed, unless you are doing many thousands of loggings in a loop.
The DEBUG and BUILD_TYPE are available and working in Android Studio.
The DEBUG boolean is the "debuggable" option in the build.gradle file:
buildTypes {
debug {
debuggable true
runProguard false
proguardFile getDefaultProguardFile('proguard-android.txt')
}
release {
debuggable false
runProguard true
proguardFile getDefaultProguardFile('proguard-android.txt')
}
}
The BUILD_TYPE is set automatically to "debug" when you run in Android Studio.
So if you set debuggable to false OR the user has a release version, then logs will be gone.
I'd recommend you look at Roboguice - http://code.google.com/p/roboguice/ - if you use the built-in Ln function, it will automatically not log on a signed APK. It simplifies a ton of other Android boilerplate as well. You can see it in action with Basedroid - https://github.com/achuinard/basedroid
You can use DebugLog class. All logs are disabled by DebugLog when the app is released. And it provides more understandable DDMS logs for developers.
Ex;
DebugLog.e("your message");